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

In this part of article we will add one more filter to our dating app, gender filter. Before adding this filter lets consider the four case that can arise in a dating world :

  1. Men looking for women ( common and frequent)
  2. Women looking for men ( common and frequent)
  3. Men looking for men ( rare and occasional)
  4. Women looking for women ( rare and occasional)

So, we have to consider above all situation while coding gender filter.

To take input of gender filter we will be using a switch component.  If switch is on the the user is looking for women and if it’s off then user is looking for men. So, this will meet all four instances we have mentioned above like this :

  1. User is male and he turns switch on, so, he is looking for women
  2. User is female and she turns switch off, so, she is looking for men
  3. User is male and he turns switch off, so, he is looking for men
  4. User is female and she turns switch on, so, she is looking for women

Now, lets add the above cases in our code. Firstly, we will add a switch component/control to our Profile screen ( component). When user toggles the switch the gender state changes in firebase database. The updated code for profile component is here :

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 } = this.props.screenProps
   this.state = {
     user: this.props.screenProps,
     SwitchGender: filterGender
   }
  }
  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 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, lets add gender filter to our match component. Below 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 */

      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 */

      /* 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 */

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

  }
  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,
  },
});

So, in the above code the members list of dating app, after passing the four filter we have coded, finally gets render by ListView component.

Here is a demo of how the gender filter feature is working :