How to build a Tinder clone using React Native and Firebase – part 1

Today we are starting new series of article,  where we will improvise the features of Tinder app. That means, it will be a tinder clone plus some awesome features that will make it even better than the Tinder app. This series is correlated to the previous series we have done, so, if you have directly landed on this page, make sure so go through previous series to get a better idea of whats going on here.

Tinder app has feature of distance filter, that is really great, but suppose you have moved to a new place where you don’t have tech savvy people around you, so, they are not using Tinder app yet. When you use tinder app in such places you won’t get any matches there. I find it as a glitch to the app.

What we will do, is that, we will make the distance feature optional. So, now user will have option to either use distance filter feature to find people around them or turn it off to even find people away from them. Because no matter where you are located, it shouldn’t stop you from finding the perfect match for you.

This feature will be helpful in one more way. When you will launch your app you will have limited user profiles. So, if distance feature is on, logged in user wont be able to see any profile but when distance feature is turned off he will get to see the other users profile.

Now lets see how we can implement it in our code. First we will add a field to the user profile ‘filterDistance’ which can be set true or false. If its true then we will then user will get profiles around then and they will be able to filter profile by the distance. If its false then distance filter will not be there and they will get profiles irrespective of distance.

First we need to update our Profile component code so that we can turn on and on the distance feature. Here 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)
   const { filterGender, filterDistance } = this.props.screenProps
   this.state = {
     user: this.props.screenProps,
     SwitchGender: filterGender,
     SwitchDistance: filterDistance
   }
  }
  componentWillUnmount() {
    const { uid } = this.state.user
    this.firebaseRef.child(uid).child('dp').off('value')
  }
  componentDidMount(){    
    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, SwitchDistance: user.filterDistance })
     })

  }
  toggleGender(value){
    let user = this.state.user
    const userData = {
      filterGender: value
    }
    firebase.database().ref('users').child(user.uid).update({ ...userData})
    this.setState({SwitchGender: value})

  }
  toggleDistance(value){
    let user = this.state.user
    const userData = {
      filterDistance: value
    }
    firebase.database().ref('users').child(user.uid).update({ ...userData})
    this.setState({SwitchDistance: value})

  }
  render() {

    const distanceButton = this.state.SwitchDistance ? (<View><Button
      onPress={() => this.props.navigation.navigate('Distance', { user: this.state.user })}
      title={ "Distance("+this.state.user.distance+"kms)" } /></View>) :(<View></View>)

    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]+'"'+")"}
        />
        <View style={{ flexDirection: "row",alignItems: "center" }}>
         <Text>Filter distance : </Text>
         <Switch onValueChange={(value) => this.toggleDistance(value) }  value={this.state.SwitchDistance} />
        </View>
        { distanceButton }
        <View style={{ flexDirection: "row",alignItems: "center" }}>
         <Text>Look for : </Text>
         <Switch onValueChange={(value) => this.toggleGender(value) }  value={this.state.SwitchGender} />
         <Text>{ this.state.SwitchGender? " female": " male" }</Text>
        </View>
      </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,
  },
});

Now, here is the updated code of 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 */
      let filterByDistance

      if(user.filterDistance){

      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

      })

    }else
      filterByDistance = filterByHeight

      /* Distance filter end */

      /* Gender filter start */

    const filterGender = _.filter(filterByDistance, profile => {

    const userShowMen = user.filterGender == false && profile.gender === 'male'
    const userShowWomen = user.filterGender == true && profile.gender === 'female'

    const profileShowMen = profile.filterGender == false && user.gender === 'male'
    const profileShowWomen = profile.filterGender == true && user.gender === 'female'

    return (userShowMen || userShowWomen) && (profileShowMen || profileShowWomen)
  })

      /* Gender filter end */

      let finalArray = filterGender

      finalArray = _.sortBy(finalArray, profile => {
        return parseInt(this.calcDistance(user.location[0],user.location[1],profile.location[0],profile.location[1]))
      })

      const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
      this.setState({ dataSource: ds.cloneWithRows(finalArray) })
    })

  }
  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 Match component above we have added two code updates. First,  we are checking the status of ‘filterDistance’ in firebase database and members are getting filtered only if the  ‘filterDistance’ is true. Second, we are sorting the final result by distance so that when the distance filter is turned off, then also logged in user gets a list of member who are most close to him by their location.

So, now our app has one great feature of which the real tinder app is short of. Below is the working demo of the new feature we have implemented :