简体   繁体   中英

Sorting Phone Contacts With React Native

My react-native application sends something similar to messages to contacts in the users phonebook that have my application. So when users register they only register to use my app, they only register their phone number. When it comes to sending a message to a contact. I access their phonebook and compare it with numbers I have in my Rails backend. If they match a number in the users phonebook. Then I pull the name from their phone phonebook and display it to them along with the number to send a message to that contact.

The issue is they are sorting randomly. I want them to sort alphabetically. The number comes from the BE (Rails) and the name comes from the FE (React-Native) app. I know I need to put them both into an array of some sort on the FE and then sort them. Unfortunatly my Javascript and React-Native skills are still somewhat weak. I know in theory what I need to do, I just can't seem to get it to work.

I have of course left out quite a bit of the code and left in just the important parts. The first part shows the three functions "parseContacts, fetchContacts, getContactName.

"fetchContacts" gets the phone number from the BE. "getContactName" gets the name from the phones phonebooks. "parseContacts" does what it says on the tin.

async parseContacts(contacts) {
    let numbers = [];
    contacts.forEach(element => {
      element.contactNumber = element.phoneNumbers[0] ? element.phoneNumbers[0].number.replace(/[^a-zA-Z0-9]/g, '') : '';
      element.phoneNumbers[0] ? numbers.push(element.phoneNumbers[0].number.replace(/[^a-zA-Z0-9]/g, '')) : null;
    });
    this.setState({ phoneContacts: contacts })
    return numbers;
  }

  async fetchContacts(contacts) {
    let phoneNumbers = await this.parseContacts(contacts);
    if (phoneNumbers.length) {
      this.setState({ isLoadingNumbers: true })
      let data = {
        "numbers": phoneNumbers,
      }
      try {
        let res = await postFunctionWithAuthToken(`${baseUrl}/contact/check`,
          JSON.stringify(data), this.state.authToken);
        if (res.response) {
          console.log('contacssssst: ', res.contact_list);
          this.setState({ contacts: res.contact_list, isLoadingNumbers: false })
        } else {
          this.setState({ isLoadingNumbers: false })
          Alert.alert('Error', `${res.error}! try again.`);
        }
      } catch (error) {
        console.log('error: ', error);
        this.setState({ isLoadingNumbers: false })
        Alert.alert('Error', 'request failed! try again.')
      }
    }
  }

  getContactName(number) {
    const { phoneContacts, countryCode } = this.state;
    if (phoneContacts.length) {
      let index = phoneContacts.findIndex(element => {
        let parsedNumber = element.phoneNumbers[0] ? element.phoneNumbers[0].number.replace(/[^a-zA-Z0-9]/g, '') : "";
        if (parsedNumber) {
          if (parsedNumber.slice(0, 1) === "0") {
            if (parsedNumber.slice(1, 2) === "0") {
              if (parsedNumber.substring(2) === number ||
                parsedNumber.substring(2).replace(`${countryCode}0`, countryCode) === number) {
                return number;
              }
            } else {
              return countryCode + parsedNumber.substring(1) === number;
            }
          } else {
            // console.log('nummm: ', parsedNumber, number);
            // return parsedNumber === number;
            if (parsedNumber === number ||
              parsedNumber.replace(`${countryCode}0`, countryCode) === number) {
              return number;
            }
          }
        }

      });
      if (Platform.OS === 'ios') {
        return index >= 0 ? phoneContacts[index].givenName : ''
      } else {
        return index >= 0 ? phoneContacts[index].displayName : ''
      }
    }
  }

The below code is where I render the name and number on the phone in a list.

            <View style={styles.container}>
              {this.state.contacts.length ?
                <FlatList
                  data={this.state.contacts}
                  extraData={this.state}
                  numColumns={1}
                  keyExtractor={(item, index) => index.toString()}
                  renderItem={({ item }) => (
                    <View style={styles.itemContainer}>
                      <View>
                        <Text style={{ color: '#000', fontSize: 20 }}>
                          {this.getContactName(item.phone_number)}</Text>
                        <Text style={{ color: '#000' }}>
                          {item.phone_number}</Text>
                      </View>
                      {item.selected ?
                        <TouchableOpacity onPress={() => this.removeContact(item)}>
                          <Icon name={"ios-add-circle"} size={26} color={primaryColor} />
                        </TouchableOpacity>
                        :
                        <TouchableOpacity onPress={() => this.selectContact(item)}>
                          <Icon name={"ios-add-circle"} size={26} color={'grey'} />
                        </TouchableOpacity>
                      }
                    </View>
                  )}
                />

Hopefully it is clear what I have done so far. What I need now I think anyway. Is another function to pull the name and number together and sort them before I render them. At least thats what I think. Maybe the sorting needs to be done in the render. I'm not 100%. Ive been trying all sorts of ways for two days now and just can't seem to get it to sort the contacts list alphabetically. Any and all help would be so greatly appreciated.

You need to put the names into the phoneNumbers array and then sort... Currently you fetch the name of the contact when rendering the phone numbers, so there's no way to sort beforehand...

    if (res.response) {
      console.log('contacssssst: ', res.contact_list);
      const contacts = res.contact_list.map((contact)=>{
//Loop through the contacts and add the display name found.
          contact.displayName = this.getContactName(contact.phone_number);
          return contact;
      });
//Sort using the displayName property.
      contacts.sort((a,b)=>{
           if(a.displayName > b.displayName){
               return 1;
           }
           if(a.displayName < b.displayName){
               return -1;
           }
           return 0;
      });
      this.setState({ contacts: res.contact_list, isLoadingNumbers: false })
    }

Then in your view you can access directly item.displayName.

I have written the sort function real quick, not too sure if that'll sort correctly.

@salketer got me very close to the right answer. I ended using a different way of sorting. And also last setState I need to set just contacts. Thanks again.

if (res.response) {
  console.log('contacts: ', res.contact_list);
    const contacts = res.contact_list.map((contact)=>{
        contact.displayName = this.getContactName(contact.phone_number);
        return contact;
    });
  contacts.sort((a, b) => a.displayName.localeCompare(b.displayName));
  console.log('contacts_returned: ', contacts);
  this.setState({ contacts, isLoadingNumbers: false })
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM