Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
7.1 kB
2
Indexable
Never
import { APIs } from '../consts/api';
import { CallLog, FavoriteInfo, PhoneNumber } from '../types/api';
import { PageRes, getData } from '../utils/request';
import Contacts from 'react-native-contacts';
import { CallRecord, Contact, NumberFavorites } from '../models';
import { getAuthToken } from 'utils/token';

export function syncCallLog(realm: Realm) {
  return getData<PageRes<CallLog>>(APIs.CALL_RECENTS + `?ver=${Date.now()}&skip=0&take=100`).then(
    (res) => {
      realm.write(() => {
        res.data.forEach((call) => {
          realm.create<CallRecord>(
            'CallRecord',
            {
              id: call.id,
              contact: call.contact,
              contact_name: call.name,
              outgoing: call.outgoing,
              video: call.type == 'video',
              created_at: call.created_at,
              state: call.state,
              duration: call.duration,
            },
            'modified'
          );
        });
      });
    }
  );
}

export function syncFavorites(realm: Realm) {
  return getData<PageRes<FavoriteInfo>>(APIs.FAVORITES + `?ver=${Date.now()}&skip=0&take=100`).then(
    (res) => {
      realm.write(() => {
        let data = realm.objects('NumberFavorites');
        realm.delete(data);
        res.data.forEach((f) => {
          realm.create<NumberFavorites>(
            'NumberFavorites',
            {
              id: f.id,
              number: f.number,
              created_at: f.created_at,
            },
            'modified'
          );
        });
      });
    }
  );
}

function build_name(c: Contacts.Contact) {
  let parts = [];
  if (c.givenName) {
    parts.push(c.givenName);
  }
  if (c.middleName) {
    parts.push(c.middleName);
  }
  if (c.familyName) {
    parts.push(c.familyName);
  }
  if (c.company) {
    parts.push(c.company);
  }
  if (parts.length == 0 && c.phoneNumbers.length > 0) {
    parts.push(c.phoneNumbers[0].number);
  }
  return parts.join(' ');
}

// normalize phone number, remove all space and contruoy code
function normalize_number(number: string) {
  return number.replace(/\s/g, '');
}

export async function syncContact(realm: Realm): Promise<string | null> {
  let now_ms = Date.now();
  let updated: Map<string, boolean> = new Map();
  let token = await getAuthToken();

  let promise1 = async () => {
    try {
      let res = await Contacts.getAll();
      return { res, error: null };
    } catch (e) {
      return { res: [], error: 'Contacts permission denied' };
    }
  };
  let promise2 = async () => {
    if (!token) return;
    try {
      let res = await getData<PageRes<PhoneNumber>>(
        APIs.CONTACTS + `?ver=${Date.now()}&skip=0&take=1000`
      );
      console.log('23232323', res);

      return { res, error: null };
    } catch (e) {
      return { res: { data: [] }, error: 'Cannot get remote contacts' };
    }
  };
  return Promise.all([promise1(), promise2()]).then(([res1, res2]) => {
    const local_contacts = res1.res;
    const local_error = res1.error;
    const remote_contacts = res2?.res;
    const remote_error = res2?.error;

    // avoid contacts duplicate
    let map = new Map<string, Contacts.Contact>();
    let local_contacts2: Contacts.Contact[] = [];
    local_contacts.forEach((contact) => {
      if (contact.phoneNumbers.length > 0) {
        let number = normalize_number(contact.phoneNumbers[0].number);
        if (!map.has(number)) {
          updated.set(number, true);
          map.set(number, contact);
          local_contacts2.push(contact);
        }
      }
    });

    // contacts returned
    realm.write(() => {
      local_contacts2.forEach((contact) => {
        if (contact.phoneNumbers.length > 0) {
          let name = build_name(contact);
          let number = normalize_number(contact.phoneNumbers[0].number);
          let exiting = realm.objectForPrimaryKey<Contact>('Contact', number);
          if (!exiting) {
            console.log('create local contact:', name);
            realm.create<Contact>(
              'Contact',
              {
                group_label: name.charAt(0).toUpperCase(),
                number,
                name: name,
                name_sort: name.toLowerCase(),
                last_updated: now_ms,
                is_local: true,
              },
              'modified'
            );
          } else if (name != exiting.name || !exiting.is_local) {
            console.log(
              'update local contact:',
              name,
              exiting.name,
              name != exiting.name,
              exiting.is_local
            );
            exiting.group_label = name.charAt(0).toUpperCase();
            exiting.name = name;
            exiting.name_sort = name.toLowerCase();
            exiting.last_updated = now_ms;
            exiting.is_local = true;
          }
        }
      });

      console.log('debug contact', remote_contacts);

      remote_contacts?.data.forEach((number) => {
        updated.set(number.number, true);
        let name = number.name || number.number;
        let remote_version = number.updated_at || number.created_at;
        let exiting = realm.objectForPrimaryKey<Contact>('Contact', number.number);
        if (!exiting) {
          console.log('create remote contact:', number.number);
          realm.create<Contact>(
            'Contact',
            {
              group_label: name.charAt(0).toUpperCase(),
              number: number.number,
              name,
              avatar: number.avatar,
              is_register: number?.is_register,
              name_sort: name.toLowerCase(),
              last_updated: now_ms,
            },
            'modified'
          );
        } else if (!exiting.is_local && exiting.last_updated < remote_version) {
          console.log('update remote contact:', number.number);
          exiting.group_label = name.charAt(0).toUpperCase();
          exiting.name = name;
          exiting.name_sort = name.toLowerCase();
          exiting.last_updated = now_ms;
        } else {
          if (exiting.is_local) {
            exiting.group_label = name.charAt(0).toUpperCase();
            exiting.name = name;
            exiting.name_sort = name.toLowerCase();
            exiting.avatar = number?.avatar;
            exiting.is_register = number?.is_register;
          }
        }
      });

      //clean up which contact not shown in updated
      realm.objects<Contact>('Contact').forEach((contact) => {
        if (!updated.has(contact.number)) {
          console.log('delete contact:', contact.number, contact.name);
          if (contact.is_local && !local_error) {
            realm.delete(contact);
          }

          if (!contact.is_local && !remote_error) {
            realm.delete(contact);
          }
        }
      });
    });

    return local_error || remote_error || null;
  });
}