diff --git a/matrixConsole/API/ContactManager.h b/matrixConsole/API/ContactManager.h index 18a42144b..42fc7c36a 100644 --- a/matrixConsole/API/ContactManager.h +++ b/matrixConsole/API/ContactManager.h @@ -1,5 +1,5 @@ /* - Copyright 2014 OpenMarket Ltd + Copyright 2015 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,46 +21,99 @@ #import "SectionedContacts.h" #import "MXCContact.h" -// warn when there is a contacts list refresh -extern NSString *const kContactManagerContactsListRefreshNotification; +/** + Posted when the contact list is loaded and updated. + The notification object is nil. + */ +extern NSString *const kContactManagerDidUpdateContactsNotification; -// the phonenumber has been internationalized -extern NSString *const kContactsDidInternationalizeNotification; +/** + Posted when contact matrix ids is updated. + The notification object is a contact Id or nil when all contacts are concerned. + */ +extern NSString *const kContactManagerDidUpdateContactMatrixIDsNotification; -@interface ContactManager : NSObject { - dispatch_queue_t processingQueue; - NSMutableDictionary* matrixIDBy3PID; -} +/** + Posted when the presence of a matrix user linked at least to one contact has changed. + The notification object is the matrix Id. The `userInfo` dictionary contains an `MXPresenceString` object under the `kContactManagerMatrixPresenceKey` key, representing the matrix user presence. + */ +extern NSString *const kContactManagerMatrixUserPresenceChangeNotification; +extern NSString *const kContactManagerMatrixPresenceKey; + +/** + Posted when all phonenumbers have been internationalized. + The notification object is nil. + */ +extern NSString *const kContactManagerDidInternationalizeNotification; + + +@interface ContactManager : NSObject + (ContactManager*)sharedManager; /** - Associated matrix session (nil by default). - This property is used to link matrix id to the contacts. + The identity server URL used to link matrix ids to the contacts according to their 3PIDs (email, phone number...). + This property is nil by default. + + If this property is not set whereas some matrix sessions are added, the identity server of the first available matrix session is used. */ -@property (nonatomic) MXSession *mxSession; +@property (nonatomic) NSString *identityServer; /** - Associated matrix REST Client (nil by default). Ignored if mxSession is defined. - This property is used to make Matrix API requests when no matrix session is provided. + Associated matrix sessions (empty by default). */ -@property (nonatomic) MXRestClient *mxRestClient; +@property (nonatomic, readonly) NSArray *mxSessions; -@property (nonatomic, readonly) NSMutableArray *contacts; +/** + The current contact list (nil by default until the device contacts are loaded). + */ +@property (nonatomic, readonly) NSArray *contacts; -// delete contacts info +/** + No by default. Set YES to update matrix ids for all the contacts in only one request when contact are loaded and an identity server is available. + */ +@property (nonatomic) BOOL enableFullMatrixIdSyncOnContactsDidLoad; + +/** + Add/remove matrix session. + */ +- (void)addMatrixSession:(MXSession*)mxSession; +- (void)removeMatrixSession:(MXSession*)mxSession; + +/** + Load and refresh the contact list. See kContactManagerDidUpdateContactsNotification posted when contact list is available. + */ +- (void)loadContacts; + +/** + Delete contacts info + */ - (void)reset; -// refresh the international phonenumber of the contacts +/** + Refresh matrix IDs for a specific contact. See kContactManagerDidUpdateContactMatrixIDsNotification + posted when update is done. + + @param contact the contact to refresh. + */ +- (void)updateMatrixIDsForContact:(MXCContact*)contact; + +/** + Refresh matrix IDs for all listed contacts. See kContactManagerDidUpdateContactMatrixIDsNotification + posted when update for all contacts is done. + */ +- (void)updateContactsMatrixIDs; + +/** + Sort the contacts in sectioned arrays to be displayable in a UITableview + */ +- (SectionedContacts *)getSectionedContacts:(NSArray*)contactList; + +/** + Refresh the international phonenumber of the contacts (See kContactManagerDidInternationalizeNotification). + + @param countryCode + */ - (void)internationalizePhoneNumbers:(NSString*)countryCode; -// refresh self.contacts -- (void)fullRefresh; - -// refresh matrix IDs -- (void)refreshContactMatrixIDs:(MXCContact*)contact; - -// sort the contacts in sectioned arrays to be displayable in a UITableview -- (SectionedContacts *)getSectionedContacts:(NSArray*)contactsList; - @end diff --git a/matrixConsole/API/ContactManager.m b/matrixConsole/API/ContactManager.m index a41623ea3..32272bfcc 100644 --- a/matrixConsole/API/ContactManager.m +++ b/matrixConsole/API/ContactManager.m @@ -22,33 +22,48 @@ #import "MXKAppSettings.h" -// warn when there is a contacts list refresh -NSString *const kContactManagerContactsListRefreshNotification = @"kContactManagerContactsListRefreshNotification"; +NSString *const kContactManagerDidUpdateContactsNotification = @"kContactManagerDidUpdateContactsNotification"; +NSString *const kContactManagerDidUpdateContactMatrixIDsNotification = @"kContactManagerDidUpdateContactMatrixIDsNotification"; -// the phonenumber has been internationalized -NSString *const kContactsDidInternationalizeNotification = @"kContactsDidInternationalizeNotification"; +NSString *const kContactManagerMatrixUserPresenceChangeNotification = @"kContactManagerMatrixUserPresenceChangeNotification"; +NSString *const kContactManagerMatrixPresenceKey = @"kContactManagerMatrixPresenceKey"; -// get the 3PIDS in one requests -//#define CONTACTS_3PIDS_SYNC 1 -// else checks the matrix IDs for each displayed contact +NSString *const kContactManagerDidInternationalizeNotification = @"kContactManagerDidInternationalizeNotification"; @interface ContactManager() { + /** + Array of `MXSession` instances. + */ + NSMutableArray *mxSessionArray; + + /** + Presence listener by matrix session + */ + NSMutableArray *mxPresenceListeners; + + /** + Matrix id linked to 3PID. + */ + NSMutableDictionary* matrixIDBy3PID; + + dispatch_queue_t processingQueue; + BOOL isLoading; NSDate *lastSyncDate; NSMutableDictionary* deviceContactByContactID; - // + // Keep history of 3PID lookup requests NSMutableArray* pending3PIDs; NSMutableArray* checked3PIDs; - - NSMutableDictionary* matrixContactByMatrixUserID; - - id matrixSessionStateObserver; } + +/** + The current REST client defined with the identity server. + */ +@property (nonatomic) MXRestClient *identityRESTClient; @end @implementation ContactManager -@synthesize contacts; #pragma mark Singleton Methods static ContactManager* sharedContactManager = nil; @@ -71,7 +86,7 @@ static ContactManager* sharedContactManager = nil; { NSString *label = [NSString stringWithFormat:@"ConsoleMatrix.%@.Contacts", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]]; - processingQueue = dispatch_queue_create([label UTF8String], NULL); + processingQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL); // save the last sync date // to avoid resync the whole phonebook @@ -86,120 +101,155 @@ static ContactManager* sharedContactManager = nil; -(void)dealloc { - - if (matrixSessionStateObserver) - { - [[NSNotificationCenter defaultCenter] removeObserver:matrixSessionStateObserver]; - matrixSessionStateObserver = nil; - } + [self reset]; [[MXKAppSettings standardAppSettings] removeObserver:self forKeyPath:@"syncLocalContacts"]; + + processingQueue = nil; } #pragma mark - -- (MXRestClient*)mxRestClient +- (void)addMatrixSession:(MXSession*)mxSession { - // Ignore `mxRestClient` property if a matrix session has been defined - if (self.mxSession) + if (!mxSession) { - return self.mxSession.matrixRestClient; + return; } - return _mxRestClient; -} - -- (void)setMxSession:(MXSession *)session -{ - // Remove potential session observer - [[NSNotificationCenter defaultCenter] removeObserver:matrixSessionStateObserver]; + // Check conditions to trigger a full refresh of contacts matrix ids + BOOL shouldUpdateContactsMatrixIDs = (self.enableFullMatrixIdSyncOnContactsDidLoad && !isLoading && !_identityRESTClient); - if (session) + if (!mxSessionArray) { - // Register session state observer - matrixSessionStateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) - { - - // Check whether the concerned session is the associated one - if (notif.object == _mxSession) - { - [self didMatrixSessionStateChange]; - } - }]; + mxSessionArray = [NSMutableArray array]; + } + if (!mxPresenceListeners) + { + mxPresenceListeners = [NSMutableArray array]; } - _mxSession = session; - - // Force update - [self didMatrixSessionStateChange]; -} - -- (void)didMatrixSessionStateChange -{ - - if (_mxSession && _mxSession.state == MXSessionStateRunning) + if ([mxSessionArray indexOfObject:mxSession] == NSNotFound) { - [self manage3PIDS]; - } -} - -#pragma mark - - -// delete contacts info -- (void)reset -{ - - contacts = nil; - - lastSyncDate = nil; - deviceContactByContactID = nil; - matrixContactByMatrixUserID = nil; - - if (matrixSessionStateObserver) - { - [[NSNotificationCenter defaultCenter] removeObserver:matrixSessionStateObserver]; - matrixSessionStateObserver = nil; - } - _mxSession = nil; - - [self saveMatrixIDsDict]; - [self saveDeviceContacts]; - [self saveContactBookInfo]; - - // warn of the contacts list update - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; -} - -// refresh the international phonenumber of the contacts -- (void)internationalizePhoneNumbers:(NSString*)countryCode -{ - - dispatch_async(processingQueue, ^{ - NSArray* contactsSnapshot = [deviceContactByContactID allValues]; + [mxSessionArray addObject:mxSession]; - for(MXCContact* contact in contactsSnapshot) - { - [contact internationalizePhonenumbers:countryCode]; + // Register a listener for on matrix presence events + id presenceListener = [mxSession listenToEventsOfTypes:@[kMXEventTypeStringPresence] + onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) { + // consider only live event + if (direction == MXEventDirectionForwards) + { + NSArray *matrixIDs = [matrixIDBy3PID allValues]; + if ([matrixIDs indexOfObject:event.userId] != NSNotFound) { + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerMatrixUserPresenceChangeNotification object:event.userId userInfo:@{kContactManagerMatrixPresenceKey:event.content[@"presence"]}]; + } + } + }]; + + [mxPresenceListeners addObject:presenceListener]; + } + + if (shouldUpdateContactsMatrixIDs) + { + [self updateContactsMatrixIDs]; + } +} + +- (void)removeMatrixSession:(MXSession*)mxSession +{ + if (!mxSession) + { + return; + } + + NSUInteger index = [mxSessionArray indexOfObject:mxSession]; + if (index != NSNotFound) + { + id presenceListener = [mxPresenceListeners objectAtIndex:index]; + [mxSession removeListener:presenceListener]; + + [mxPresenceListeners removeObjectAtIndex:index]; + [mxSessionArray removeObjectAtIndex:index]; + + // Reset the current rest client (It will be rebuild if need) + _identityRESTClient = nil; + + // Reset history of 3PID lookup requests + pending3PIDs = nil; + checked3PIDs = nil; + } +} + +- (NSArray*)mxSessions +{ + return [NSArray arrayWithArray:mxSessionArray]; +} + +- (NSArray*)contacts +{ + // Return nil if the loading step is in progress. + if (isLoading) + { + return nil; + } + + return [deviceContactByContactID allValues]; +} + +- (void)setIdentityServer:(NSString *)identityServer +{ + _identityServer = identityServer; + + if (identityServer) + { + _identityRESTClient = [[MXRestClient alloc] initWithHomeServer:nil]; + _identityRESTClient.identityServer = identityServer; + + if (self.enableFullMatrixIdSyncOnContactsDidLoad) { + [self updateContactsMatrixIDs]; } - - [self saveDeviceContacts]; - - [[NSNotificationCenter defaultCenter] postNotificationName:kContactsDidInternationalizeNotification object:nil userInfo:nil]; - }); + } + else + { + _identityRESTClient = nil; + } + + // Reset history of 3PID lookup requests + pending3PIDs = nil; + checked3PIDs = nil; } -- (void)fullRefresh +- (MXRestClient*)identityRESTClient { + if (!_identityRESTClient) + { + if (self.identityServer) + { + _identityRESTClient = [[MXRestClient alloc] initWithHomeServer:nil]; + _identityRESTClient.identityServer = self.identityServer; + } + else if (mxSessionArray.count) + { + MXSession *mxSession = [mxSessionArray firstObject]; + _identityRESTClient = [[MXRestClient alloc] initWithHomeServer:nil]; + _identityRESTClient.identityServer = mxSession.matrixRestClient.identityServer; + } + } + return _identityRESTClient; +} + +#pragma mark - + +- (void)loadContacts +{ // check if the user allowed to sync local contacts if (![[MXKAppSettings standardAppSettings] syncLocalContacts]) { - contacts = nil; // if the user did not allow to sync local contacts // ignore this sync - // at least, display the known contacts - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidUpdateContactsNotification object:nil userInfo:nil]; return; } @@ -214,10 +264,9 @@ static ContactManager* sharedContactManager = nil; if (ab) { - ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) - { + ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) { dispatch_async(dispatch_get_main_queue(), ^{ - [self fullRefresh]; + [self loadContacts]; }); }); @@ -228,8 +277,11 @@ static ContactManager* sharedContactManager = nil; return; } - pending3PIDs = [[NSMutableArray alloc] init]; - checked3PIDs = [[NSMutableArray alloc] init]; + isLoading = YES; + + // Reset history of 3PID lookup requests + pending3PIDs = nil; + checked3PIDs = nil; // cold start // launch the dict from the file system @@ -258,15 +310,13 @@ static ContactManager* sharedContactManager = nil; BOOL contactBookUpdate = NO; - NSMutableArray* deletedContactIDs = [[deviceContactByContactID allKeys] mutableCopy]; + NSMutableArray* deletedContactIDs = [NSMutableArray arrayWithArray:[deviceContactByContactID allKeys]]; - // can list tocal contacts + // can list local contacts? if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) { - NSString* countryCode = [[MXKAppSettings standardAppSettings] phonebookCountryCode]; - ABAddressBookRef ab = ABAddressBookCreateWithOptions(nil, nil); ABRecordRef contactRecord; CFIndex index; @@ -278,7 +328,6 @@ static ContactManager* sharedContactManager = nil; for (index = 0; index < peopleCount; index++) { - contactRecord = (ABRecordRef)CFArrayGetValueAtIndex(people, index); NSString* contactID = [MXCContact contactID:contactRecord]; @@ -337,55 +386,292 @@ static ContactManager* sharedContactManager = nil; lastSyncDate = [NSDate date]; [self saveContactBookInfo]; - NSMutableArray* deviceContacts = [[deviceContactByContactID allValues] mutableCopy]; - - if (_mxSession && _mxSession.state == MXSessionStateRunning) - { - [self manage3PIDS]; - } - else - { - // display what you could have read - dispatch_async(dispatch_get_main_queue(), ^{ - contacts = deviceContacts; - - // at least, display the known contacts - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; - }); - } - }); -} - -// the local contacts are listed -// update their 3PIDs and their update -- (void) manage3PIDS -{ - dispatch_async(processingQueue, ^{ - NSMutableArray* tmpContacts = nil; - - // update with the known dict 3PID -> matrix ID + // Update loaded contacts with the known dict 3PID -> matrix ID [self updateMatrixIDDeviceContacts]; - tmpContacts = [[deviceContactByContactID allValues] mutableCopy]; - dispatch_async(dispatch_get_main_queue(), ^{ - // stored self.contacts in the right thread - contacts = tmpContacts; -#if CONTACTS_3PIDS_SYNC - // refresh the 3PIDS -> matrix IDs - [self refreshMatrixIDs]; -#else - // nop - // wait that refreshContactMatrixIDs is called + // Contacts are loaded, post a notification + isLoading = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidUpdateContactsNotification object:nil userInfo:nil]; -#endif - // at least, display the known contacts - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; + if (self.enableFullMatrixIdSyncOnContactsDidLoad) { + [self updateContactsMatrixIDs]; + } }); }); } -- (void) updateContactMatrixIDs:(MXCContact*) contact +// refresh matrix IDs +- (void)updateMatrixIDsForContact:(MXCContact *)contact +{ + if (!contact.isMatrixContact && self.identityRESTClient) + { + if (!pending3PIDs) + { + pending3PIDs = [[NSMutableArray alloc] init]; + checked3PIDs = [[NSMutableArray alloc] init]; + } + + // Retrieve all 3PIDs of the contact by checking pending requests + NSMutableArray* pids = [[NSMutableArray alloc] init]; + NSMutableArray* medias = [[NSMutableArray alloc] init]; + for(MXCEmail* email in contact.emailAddresses) + { + if (([pending3PIDs indexOfObject:email.emailAddress] == NSNotFound) && ([checked3PIDs indexOfObject:email.emailAddress] == NSNotFound)) + { + [pids addObject:email.emailAddress]; + [medias addObject:@"email"]; + } + } + + if (pids.count > 0) + { + [pending3PIDs addObjectsFromArray:pids]; + + [self.identityRESTClient lookup3pids:pids + forMedia:medias + success:^(NSArray *userIds) { + // sanity check + if (userIds.count == pids.count) + { + // Update status table + [checked3PIDs addObjectsFromArray:pids]; + for(NSString* pid in pids) + { + [pending3PIDs removeObject:pid]; + } + + // Look for updates + BOOL isUpdated = NO; + for (int index = 0; index < pids.count; index++) + { + id matrixID = [userIds objectAtIndex:index]; + NSString* pid = [pids objectAtIndex:index]; + NSString *currentMatrixID = [matrixIDBy3PID valueForKey:pid]; + + if ([matrixID isEqual:[NSNull null]]) + { + if (currentMatrixID) + { + [matrixIDBy3PID removeObjectForKey:pid]; + isUpdated = YES; + } + } + else if ([matrixID isKindOfClass:[NSString class]]) + { + if (![currentMatrixID isEqualToString:matrixID]) + { + [matrixIDBy3PID setValue:matrixID forKey:pid]; + isUpdated = YES; + } + } + } + + if (isUpdated) + { + [self saveMatrixIDsDict]; + } + + // Update only this contact + [self updateContactMatrixIDs:contact]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidUpdateContactMatrixIDsNotification object:contact.contactID userInfo:nil]; + }); + } + } + failure:^(NSError *error) { + NSLog(@"[ContactManager] lookup3pids failed %@", error); + + // try later + dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [self updateMatrixIDsForContact:contact]; + }); + }]; + } + } +} + + +- (void)updateContactsMatrixIDs +{ + // Check if at least an identity server is available, and if the loading step is not in progress + if (!self.identityRESTClient || isLoading) + { + return; + } + + // Refresh the 3PIDs -> Matrix ID mapping + dispatch_async(processingQueue, ^{ + + NSArray* contactsSnapshot = [deviceContactByContactID allValues]; + + // Retrieve all 3PIDs + NSMutableArray* pids = [[NSMutableArray alloc] init]; + NSMutableArray* medias = [[NSMutableArray alloc] init]; + for(MXCContact* contact in contactsSnapshot) + { + // the phonenumbers are not managed + /*for(ConsolePhoneNumber* pn in contact.phoneNumbers) + { + if (pn.textNumber.length > 0) + { + + // not yet added + if ([pids indexOfObject:pn.textNumber] == NSNotFound) + { + [pids addObject:pn.textNumber]; + [medias addObject:@"msisdn"]; + } + } + }*/ + + for(MXCEmail* email in contact.emailAddresses) + { + if (email.emailAddress.length > 0) + { + // not yet added + if ([pids indexOfObject:email.emailAddress] == NSNotFound) + { + [pids addObject:email.emailAddress]; + [medias addObject:@"email"]; + } + } + } + } + + // Update 3PIDs mapping + if (pids.count > 0) + { + [self.identityRESTClient lookup3pids:pids + forMedia:medias + success:^(NSArray *userIds) { + // Sanity check + if (userIds.count == pids.count) + { + matrixIDBy3PID = [[NSMutableDictionary alloc] initWithObjects:userIds forKeys:pids]; + [self saveMatrixIDsDict]; + [self updateMatrixIDDeviceContacts]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidUpdateContactMatrixIDsNotification object:nil userInfo:nil]; + }); + } + } + failure:^(NSError *error) { + NSLog(@"[ContactManager] lookup3pids failed %@", error); + + // try later + dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [self updateContactsMatrixIDs]; + }); + }]; + } + else + { + matrixIDBy3PID = nil; + [self saveMatrixIDsDict]; + } + }); +} + +- (void)reset +{ +// [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionStateDidChangeNotification object:nil]; + + isLoading = NO; + matrixIDBy3PID = nil; + [self saveMatrixIDsDict]; + + deviceContactByContactID = nil; + [self saveDeviceContacts]; + + lastSyncDate = nil; + [self saveContactBookInfo]; + + while (mxSessionArray.count) { + [self removeMatrixSession:mxSessionArray.lastObject]; + } + mxSessionArray = nil; + mxPresenceListeners = nil; + _identityServer = nil; + _identityRESTClient = nil; + + pending3PIDs = nil; + checked3PIDs = nil; + + // warn of the contacts list update + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidUpdateContactsNotification object:nil userInfo:nil]; +} + +// refresh the international phonenumber of the contacts +- (void)internationalizePhoneNumbers:(NSString*)countryCode +{ + dispatch_async(processingQueue, ^{ + NSArray* contactsSnapshot = [deviceContactByContactID allValues]; + + for(MXCContact* contact in contactsSnapshot) + { + [contact internationalizePhonenumbers:countryCode]; + } + + [self saveDeviceContacts]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerDidInternationalizeNotification object:nil userInfo:nil]; + }); + }); +} + +- (SectionedContacts *)getSectionedContacts:(NSArray*)contactsList +{ + UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation]; + + int indexOffset = 0; + + NSInteger index, sectionTitlesCount = [[collation sectionTitles] count]; + NSMutableArray *tmpSectionsArray = [[NSMutableArray alloc] initWithCapacity:(sectionTitlesCount)]; + + sectionTitlesCount += indexOffset; + + for (index = 0; index < sectionTitlesCount; index++) + { + NSMutableArray *array = [[NSMutableArray alloc] init]; + [tmpSectionsArray addObject:array]; + } + + int contactsCount = 0; + + for (MXCContact *aContact in contactsList) + { + NSInteger section = [collation sectionForObject:aContact collationStringSelector:@selector(displayName)] + indexOffset; + + [[tmpSectionsArray objectAtIndex:section] addObject:aContact]; + ++contactsCount; + } + + NSMutableArray *tmpSectionedContactsTitle = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount]; + NSMutableArray *shortSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount]; + + for (index = indexOffset; index < sectionTitlesCount; index++) + { + + NSMutableArray *usersArrayForSection = [tmpSectionsArray objectAtIndex:index]; + + if ([usersArrayForSection count] != 0) + { + NSArray* sortedUsersArrayForSection = [collation sortedArrayFromArray:usersArrayForSection collationStringSelector:@selector(displayName)]; + [shortSectionsArray addObject:sortedUsersArrayForSection]; + [tmpSectionedContactsTitle addObject:[[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:(index - indexOffset)]]; + } + } + + return [[SectionedContacts alloc] initWithContacts:shortSectionsArray andTitles:tmpSectionedContactsTitle andCount:contactsCount]; +} + +#pragma mark - Internals + +- (void)updateContactMatrixIDs:(MXCContact*) contact { // the phonenumbers wil be managed later /*for(ConsolePhoneNumber* pn in contact.phoneNumbers) @@ -418,9 +704,8 @@ static ContactManager* sharedContactManager = nil; } } -- (void) updateMatrixIDDeviceContacts +- (void)updateMatrixIDDeviceContacts { - NSArray* deviceContacts = [deviceContactByContactID allValues]; // update the contacts info @@ -430,221 +715,6 @@ static ContactManager* sharedContactManager = nil; } } -#ifdef CONTACTS_3PIDS_SYNC -// refresh the 3PIDs -> Matrix ID list -// update the contact is required -- (void)refreshMatrixIDs -{ - - // build the request parameters - NSMutableArray* pids = [[NSMutableArray alloc] init]; - NSMutableArray* medias = [[NSMutableArray alloc] init]; - - for(MXCContact* contact in deviceContactsList) - { - // the phonenumbers are not managed - /*for(ConsolePhoneNumber* pn in contact.phoneNumbers) - { - if (pn.textNumber.length > 0) - { - - // not yet added - if ([pids indexOfObject:pn.textNumber] == NSNotFound) - { - [pids addObject:pn.textNumber]; - [medias addObject:@"msisdn"]; - } - } - }*/ - - for(MXCEmail* email in contact.emailAddresses) - { - if (email.emailAddress.length > 0) - { - - // not yet added - if ([pids indexOfObject:email.emailAddress] == NSNotFound) - { - [pids addObject:email.emailAddress]; - [medias addObject:@"email"]; - } - } - } - } - - // get some pids - if (pids.count > 0) - { - if (self.mxRestClient) - { - [self.mxRestClient lookup3pids:pids - forMedia:medias - success:^(NSArray *userIds) - { - // sanity check - if (userIds.count == pids.count) - { - - matrixIDBy3PID = [[NSMutableDictionary alloc] initWithObjects:userIds forKeys:pids]; - [self saveMatrixIDsDict]; - [self updateMatrixIDDeviceContactsList]; - - // add the MX users - NSMutableArray* tmpContacts = [deviceContactsList mutableCopy]; - [self mergeMXUsers:tmpContacts]; - - dispatch_async(dispatch_get_main_queue(), ^{ - contacts = tmpContacts; - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; - }); - } - } - failure:^(NSError *error) - { - // try later - dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self refreshMatrixIDs]; - }); - } - ]; - } - } -} -#endif - -// refresh matrix IDs -- (void)refreshContactMatrixIDs:(MXCContact*)contact -{ -#ifndef CONTACTS_3PIDS_SYNC - if (!contact.isMatrixContact) - { - - // check pending requests - NSMutableArray* pids = [[NSMutableArray alloc] init]; - NSMutableArray* medias = [[NSMutableArray alloc] init]; - - for(MXCEmail* email in contact.emailAddresses) - { - if (([pending3PIDs indexOfObject:email.emailAddress] == NSNotFound) && ([checked3PIDs indexOfObject:email.emailAddress] == NSNotFound)) - { - [pids addObject:email.emailAddress]; - [medias addObject:@"email"]; - } - } - - if (pids.count > 0) - { - [pending3PIDs addObjectsFromArray:pids]; - - if (self.mxRestClient) - { - [self.mxRestClient lookup3pids:pids - forMedia:medias - success:^(NSArray *userIds) - { - // sanity check - if (userIds.count == pids.count) - { - - // update statuses table - [checked3PIDs addObjectsFromArray:pids]; - for(NSString* pid in pids) - { - [pending3PIDs removeObject:pid]; - } - - BOOL isUpdated = NO; - NSMutableArray* matrixContactsToRemove = [[NSMutableArray alloc] init]; - - // apply updates - if (pids.count > 0) - { - for(int index = 0; index < pids.count; index++) - { - NSString* matrixID = [userIds objectAtIndex:index]; - NSString* pid = [pids objectAtIndex:index]; - - // the dict is created on demand - if (!matrixIDBy3PID) - { - [self loadMatrixIDsDict]; - } - - id currentMatrixID = [matrixIDBy3PID valueForKey:pid]; - - // do not keep useless info - if ([matrixID isKindOfClass:[NSString class]]) - { - - // do not update if not required - if (![currentMatrixID isKindOfClass:[NSString class]] || ![(NSString*)currentMatrixID isEqualToString:matrixID]) - { - [matrixIDBy3PID setValue:matrixID forKey:pid]; - isUpdated = YES; - } - - } - else - { - if (currentMatrixID) - { - [matrixIDBy3PID removeObjectForKey:pid]; - isUpdated = YES; - } - } - - // is there a matrix contact with the same - if ([matrixContactByMatrixUserID objectForKey:matrixID]) - { - [matrixContactsToRemove addObject:[matrixContactByMatrixUserID objectForKey:matrixID]]; - } - } - - if (isUpdated) - { - [self saveMatrixIDsDict]; - } - } - - // some matrix contacts will be replaced by this contact - if (matrixContactsToRemove.count > 0) - { - [self updateContactMatrixIDs:contact]; - - for(MXCContact* contactToRemove in matrixContactsToRemove) - { - [self.contacts removeObject:contactToRemove]; - } - - // warn there is a global refresh - [[NSNotificationCenter defaultCenter] postNotificationName:kContactManagerContactsListRefreshNotification object:nil userInfo:nil]; - } - else - { - // update only this contact - [self updateContactMatrixIDs:contact]; - } - } - } - failure:^(NSError *error) - { - // try later - dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self refreshContactMatrixIDs:contact]; - }); - }]; - } - else - { - dispatch_after(dispatch_walltime(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self refreshContactMatrixIDs:contact]; - }); - } - } - } -#endif -} - #pragma mark - KVO - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context @@ -652,58 +722,11 @@ static ContactManager* sharedContactManager = nil; if ([@"syncLocalContacts" isEqualToString:keyPath]) { dispatch_async(dispatch_get_main_queue(), ^{ - [self fullRefresh]; + [self loadContacts]; }); } } -- (SectionedContacts *)getSectionedContacts:(NSArray*)contactsList -{ - UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation]; - - int indexOffset = 0; - - NSInteger index, sectionTitlesCount = [[collation sectionTitles] count]; - NSMutableArray *tmpSectionsArray = [[NSMutableArray alloc] initWithCapacity:(sectionTitlesCount)]; - - sectionTitlesCount += indexOffset; - - for (index = 0; index < sectionTitlesCount; index++) - { - NSMutableArray *array = [[NSMutableArray alloc] init]; - [tmpSectionsArray addObject:array]; - } - - int contactsCount = 0; - - for (MXCContact *aContact in contactsList) - - { - NSInteger section = [collation sectionForObject:aContact collationStringSelector:@selector(displayName)] + indexOffset; - - [[tmpSectionsArray objectAtIndex:section] addObject:aContact]; - ++contactsCount; - } - - NSMutableArray *tmpSectionedContactsTitle = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount]; - NSMutableArray *shortSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount]; - - for (index = indexOffset; index < sectionTitlesCount; index++) - { - - NSMutableArray *usersArrayForSection = [tmpSectionsArray objectAtIndex:index]; - - if ([usersArrayForSection count] != 0) - { - NSArray* sortedUsersArrayForSection = [collation sortedArrayFromArray:usersArrayForSection collationStringSelector:@selector(displayName)]; - [shortSectionsArray addObject:sortedUsersArrayForSection]; - [tmpSectionedContactsTitle addObject:[[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:(index - indexOffset)]]; - } - } - - return [[SectionedContacts alloc] initWithContacts:shortSectionsArray andTitles:tmpSectionedContactsTitle andCount:contactsCount]; -} - #pragma mark - file caches static NSString *matrixIDsDictFile = @"matrixIDsDict"; @@ -717,7 +740,6 @@ static NSString *contactsBookInfoFile = @"contacts"; NSString *dataFilePath = [documentsDirectory stringByAppendingPathComponent:matrixIDsDictFile]; if (matrixIDBy3PID) - { NSMutableData *theData = [NSMutableData data]; NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData]; @@ -728,7 +750,6 @@ static NSString *contactsBookInfoFile = @"contacts"; [theData writeToFile:dataFilePath atomically:YES]; } else - { NSFileManager *fileManager = [[NSFileManager alloc] init]; [fileManager removeItemAtPath:dataFilePath error:nil]; @@ -744,7 +765,6 @@ static NSString *contactsBookInfoFile = @"contacts"; NSFileManager *fileManager = [[NSFileManager alloc] init]; if ([fileManager fileExistsAtPath:dataFilePath]) - { // the file content could be corrupted @try @@ -761,7 +781,8 @@ static NSString *contactsBookInfoFile = @"contacts"; } [decoder finishDecoding]; - } @catch (NSException *exception) + } + @catch (NSException *exception) { } } @@ -772,14 +793,13 @@ static NSString *contactsBookInfoFile = @"contacts"; } } -- (void) saveDeviceContacts +- (void)saveDeviceContacts { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *dataFilePath = [documentsDirectory stringByAppendingPathComponent:localContactsFile]; if (deviceContactByContactID && (deviceContactByContactID.count > 0)) - { NSMutableData *theData = [NSMutableData data]; NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData]; @@ -791,7 +811,6 @@ static NSString *contactsBookInfoFile = @"contacts"; [theData writeToFile:dataFilePath atomically:YES]; } else - { NSFileManager *fileManager = [[NSFileManager alloc] init]; [fileManager removeItemAtPath:dataFilePath error:nil]; @@ -807,7 +826,6 @@ static NSString *contactsBookInfoFile = @"contacts"; NSFileManager *fileManager = [[NSFileManager alloc] init]; if ([fileManager fileExistsAtPath:dataFilePath]) - { // the file content could be corrupted @try @@ -890,5 +908,4 @@ static NSString *contactsBookInfoFile = @"contacts"; } } - @end diff --git a/matrixConsole/AppDelegate.m b/matrixConsole/AppDelegate.m index 4d45c4129..368c1c03f 100644 --- a/matrixConsole/AppDelegate.m +++ b/matrixConsole/AppDelegate.m @@ -253,7 +253,8 @@ } // refresh the contacts list - [[ContactManager sharedManager] fullRefresh]; + [ContactManager sharedManager].enableFullMatrixIdSyncOnContactsDidLoad = NO; + [[ContactManager sharedManager] loadContacts]; _isAppForeground = YES; @@ -362,12 +363,8 @@ // Check whether the concerned session is a new one if (mxSession.state == MXSessionStateInitialised) { - if (!self.masterTabBarController.mxSessions.count) { - // Report this first session to contact manager - [[ContactManager sharedManager] setMxSession:mxSession]; - - // TODO GFO handle multi-session in ContactManager - } + // Report this session to contact manager + [[ContactManager sharedManager] addMatrixSession:mxSession]; // Update all view controllers thanks to tab bar controller [self.masterTabBarController addMatrixSession:mxSession]; @@ -378,6 +375,7 @@ [self enableInAppNotifications:YES]; } } else if (mxSession.state == MXSessionStateClosed) { + [[ContactManager sharedManager] removeMatrixSession:mxSession]; [self.masterTabBarController removeMatrixSession:mxSession]; } diff --git a/matrixConsole/Model/MXCContactField.h b/matrixConsole/Model/MXCContactField.h index 275815849..2eaeb12ef 100644 --- a/matrixConsole/Model/MXCContactField.h +++ b/matrixConsole/Model/MXCContactField.h @@ -1,5 +1,5 @@ /* - Copyright 2014 OpenMarket Ltd + Copyright 2015 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ // contact ID where the email has been found @property (nonatomic, readonly) NSString *contactID; + // linked matrix account @property (nonatomic, readwrite) NSString *matrixID; @property (nonatomic, readonly) UIImage *avatarImage; diff --git a/matrixConsole/Model/MXCContactField.m b/matrixConsole/Model/MXCContactField.m index 03bfdd3fc..d8d5cb06f 100644 --- a/matrixConsole/Model/MXCContactField.m +++ b/matrixConsole/Model/MXCContactField.m @@ -1,5 +1,5 @@ /* - Copyright 2014 OpenMarket Ltd + Copyright 2015 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,24 +20,28 @@ #import "ContactManager.h" -@interface MXCContactField() { +@interface MXCContactField() +{ NSString* avatarURL; } @end @implementation MXCContactField -- (void)initFields { +- (void)initFields +{ // init members _contactID = nil; _matrixID = nil; avatarURL = @""; } -- (id)initWithContactID:(NSString*)contactID matrixID:(NSString*)matrixID { +- (id)initWithContactID:(NSString*)contactID matrixID:(NSString*)matrixID +{ self = [super init]; - if (self) { + if (self) + { [self initFields]; _contactID = contactID; _matrixID = matrixID; @@ -45,15 +49,18 @@ return self; } -- (void)dealloc { +- (void)dealloc +{ // remove the observers [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)setMatrixID:(NSString*)aMatrixID { +- (void)setMatrixID:(NSString*)aMatrixID +{ // check if there is an update // nil test + string comparison - if ((aMatrixID != _matrixID) && ![aMatrixID isEqualToString:_matrixID]) { + if ((aMatrixID != _matrixID) && ![aMatrixID isEqualToString:_matrixID]) + { _matrixID = aMatrixID; dispatch_async(dispatch_get_main_queue(), ^{ @@ -62,74 +69,99 @@ } } -- (void)loadAvatarWithSize:(CGSize)avatarSize { - - // the avatar image is already done - if (_avatarImage) { +- (void)loadAvatarWithSize:(CGSize)avatarSize +{ + // Check whether the avatar image is already set + if (_avatarImage) + { return; } - // sanity check - if (_matrixID) { + // Sanity check + if (_matrixID) + { // nil -> there is no avatar - if (!avatarURL) { + if (!avatarURL) + { return; } - // empty string means not yet initialized - if (avatarURL.length > 0) { + // Empty string means not yet initialized + if (avatarURL.length > 0) + { [self downloadAvatarImage]; - } else { - // TODO GFO Handle multi-session here - ContactManager *contactManager = [ContactManager sharedManager]; + } + else + { + // Consider here all sessions reported into contact manager + NSArray* mxSessions = [ContactManager sharedManager].mxSessions; - // check if the user is already known - MXUser* user = [contactManager.mxSession userWithUserId:_matrixID]; - - if (user) { - avatarURL = [contactManager.mxSession.matrixRestClient urlOfContentThumbnail:user.avatarUrl toFitViewSize:avatarSize withMethod:MXThumbnailingMethodCrop]; - [self downloadAvatarImage]; + if (mxSessions.count) + { + // Check whether a matrix user is already known + MXUser* user; - } else { + for (MXSession *mxSession in mxSessions) + { + user = [mxSession userWithUserId:_matrixID]; + if (user) + { + avatarURL = [mxSession.matrixRestClient urlOfContentThumbnail:user.avatarUrl toFitViewSize:avatarSize withMethod:MXThumbnailingMethodCrop]; + [self downloadAvatarImage]; + break; + } + } - if (contactManager.mxRestClient) { - [contactManager.mxRestClient avatarUrlForUser:_matrixID - success:^(NSString *avatarUrl) { - avatarURL = [contactManager.mxSession.matrixRestClient urlOfContentThumbnail:avatarUrl toFitViewSize:avatarSize withMethod:MXThumbnailingMethodCrop]; - [self downloadAvatarImage]; - } - failure:^(NSError *error) { - // - }]; + + if (!user) + + { + MXSession *mxSession = mxSessions.firstObject; + [mxSession.matrixRestClient avatarUrlForUser:_matrixID + success:^(NSString *avatarUrl) + { + avatarURL = [mxSession.matrixRestClient urlOfContentThumbnail:avatarUrl toFitViewSize:avatarSize withMethod:MXThumbnailingMethodCrop]; + [self downloadAvatarImage]; + } + failure:^(NSError *error) + { + // + }]; } } } } } -- (void)downloadAvatarImage { - +- (void)downloadAvatarImage +{ // the avatar image is already done - if (_avatarImage) { + if (_avatarImage) + { return; } - if (avatarURL.length > 0) { + if (avatarURL.length > 0) + { NSString *cacheFilePath = [MXKMediaManager cachePathForMediaWithURL:avatarURL inFolder:kMXKMediaManagerAvatarThumbnailFolder]; _avatarImage = [MXKMediaManager loadPictureFromFilePath:cacheFilePath]; // the image is already in the cache - if (_avatarImage) { + if (_avatarImage) + { dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:kMXCContactThumbnailUpdateNotification object:_contactID userInfo:nil]; }); - } else { + } + else + { MXKMediaLoader* loader = [MXKMediaManager existingDownloaderWithOutputFilePath:cacheFilePath]; - if (!loader) { + if (!loader) + { [MXKMediaManager downloadMediaFromURL:avatarURL andSaveAtFilePath:cacheFilePath]; } @@ -138,16 +170,20 @@ } } -- (void)onMediaDownloadEnd:(NSNotification *)notif { +- (void)onMediaDownloadEnd:(NSNotification *)notif +{ // sanity check - if ([notif.object isKindOfClass:[NSString class]]) { + if ([notif.object isKindOfClass:[NSString class]]) + { NSString* url = notif.object; NSString* cacheFilePath = notif.userInfo[kMXKMediaLoaderFilePathKey]; - if ([url isEqualToString:avatarURL] && cacheFilePath.length) { + if ([url isEqualToString:avatarURL] && cacheFilePath.length) + { // update the image UIImage* image = [MXKMediaManager loadPictureFromFilePath:cacheFilePath]; - if (image) { + if (image) + { _avatarImage = image; dispatch_async(dispatch_get_main_queue(), ^{ @@ -162,7 +198,8 @@ - (id)initWithCoder:(NSCoder *)coder { - if (self) { + if (self) + { [self initFields]; _contactID = [coder decodeObjectForKey:@"contactID"]; _matrixID = [coder decodeObjectForKey:@"matrixID"]; @@ -171,7 +208,8 @@ return self; } -- (void)encodeWithCoder:(NSCoder *)coder { +- (void)encodeWithCoder:(NSCoder *)coder +{ [coder encodeObject:_contactID forKey:@"contactID"]; [coder encodeObject:_matrixID forKey:@"matrixID"]; } diff --git a/matrixConsole/View/ContactTableCell.h b/matrixConsole/View/ContactTableCell.h index 5b69e2135..94c75bfb1 100644 --- a/matrixConsole/View/ContactTableCell.h +++ b/matrixConsole/View/ContactTableCell.h @@ -27,7 +27,10 @@ @property (strong, nonatomic) IBOutlet UILabel *matrixIDLabel; @property (strong, nonatomic) IBOutlet UIImageView *matrixUserIconView; -// reference to the linked message +/** + The contact displayed in the table view cell. + Set this property nil to dispose listeners and other resources. + */ @property (strong, nonatomic) MXCContact *contact; @end diff --git a/matrixConsole/View/ContactTableCell.m b/matrixConsole/View/ContactTableCell.m index 6fe7fd3c1..f97a23fc3 100644 --- a/matrixConsole/View/ContactTableCell.m +++ b/matrixConsole/View/ContactTableCell.m @@ -24,7 +24,7 @@ @interface ContactTableCell() { - id membersListener; + id mxPresenceObserver; } @end @@ -32,85 +32,63 @@ - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - if (membersListener) - { - // TODO GFO: use the right mxSession in case of multi-session - [[ContactManager sharedManager].mxSession removeListener:membersListener]; - membersListener = nil; - } } -- (void)setContact:(MXCContact *)aContact +- (void)setContact:(MXCContact *)contact { - - _contact = aContact; + _contact = contact; // remove any pending observers [[NSNotificationCenter defaultCenter] removeObserver:self]; - - // remove the matrix info until they are retrieved from the Matrix SDK - if (membersListener) - { - [[ContactManager sharedManager].mxSession removeListener:membersListener]; - membersListener = nil; + if (mxPresenceObserver) { + [[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver]; + mxPresenceObserver = nil; } + self.thumbnailView.layer.borderWidth = 0; - // be warned when the matrix ID and the thumbnail is updated - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMatrixIdUpdate:) name:kMXCContactMatrixIdentifierUpdateNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXCContactThumbnailUpdateNotification object:nil]; - - // Register a listener for events that concern room members - NSArray *mxMembersEvents = @[ - kMXEventTypeStringPresence - ]; - membersListener = [[ContactManager sharedManager].mxSession listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) - { - // consider only live event - if (direction == MXEventDirectionForwards) - { - NSString* matrixUserID = nil; - - // get the matrix identifiers - NSArray* matrixIdentifiers = self.contact.matrixIdentifiers; - - if (matrixIdentifiers.count > 0) - { - matrixUserID = [self.contact.matrixIdentifiers objectAtIndex:0]; - } - - // the event is the current user event - if (matrixUserID && [matrixUserID isEqualToString:event.userId]) - { - [self refreshPresenceUserRing:[MXTools presence:event.content[@"presence"]]]; - } - } - }]; - - // init the contact info - [[ContactManager sharedManager] refreshContactMatrixIDs:_contact]; - - NSArray* matrixIDs = _contact.matrixIdentifiers; - - if (matrixIDs.count == 1) - { - self.contactDisplayNameLabel.hidden = YES; + if (contact) { + // be warned when the matrix ID and the thumbnail is updated + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMatrixIdUpdate:) name:kMXCContactMatrixIdentifierUpdateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXCContactThumbnailUpdateNotification object:nil]; - self.matrixDisplayNameLabel.hidden = NO; - self.matrixDisplayNameLabel.text = _contact.displayName; - self.matrixIDLabel.hidden = NO; - self.matrixIDLabel.text = [ _contact.matrixIdentifiers objectAtIndex:0]; + mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + // get the matrix identifiers + NSArray* matrixIdentifiers = self.contact.matrixIdentifiers; + if (matrixIdentifiers.count > 0) + { + // Consider only the first id + NSString *matrixUserID = matrixIdentifiers.firstObject; + if ([matrixUserID isEqualToString:notif.object]) + { + [self refreshPresenceUserRing:[MXTools presence:[notif.userInfo objectForKey:kContactManagerMatrixPresenceKey]]]; + } + } + }]; - } - else - { - self.contactDisplayNameLabel.hidden = NO; - self.contactDisplayNameLabel.text = _contact.displayName; + // Refresh matrix info of the contact + [[ContactManager sharedManager] updateMatrixIDsForContact:_contact]; - self.matrixDisplayNameLabel.hidden = YES; - self.matrixIDLabel.hidden = YES; + NSArray* matrixIDs = _contact.matrixIdentifiers; + + if (matrixIDs.count == 1) + { + self.contactDisplayNameLabel.hidden = YES; + + self.matrixDisplayNameLabel.hidden = NO; + self.matrixDisplayNameLabel.text = _contact.displayName; + self.matrixIDLabel.hidden = NO; + self.matrixIDLabel.text = [ _contact.matrixIdentifiers objectAtIndex:0]; + } + else + { + self.contactDisplayNameLabel.hidden = NO; + self.contactDisplayNameLabel.text = _contact.displayName; + + self.matrixDisplayNameLabel.hidden = YES; + self.matrixIDLabel.hidden = YES; + } } [self refreshContactThumbnail]; @@ -119,27 +97,27 @@ - (void)refreshUserPresence { - - // search the linked mxUser + // Look for a potential matrix user linked with this contact NSArray* matrixIdentifiers = self.contact.matrixIdentifiers; - - // if defined if (matrixIdentifiers.count > 0) { - // get the first matrix identifier - NSString* matrixUserID = [self.contact.matrixIdentifiers objectAtIndex:0]; + // Consider only the first matrix identifier + NSString* matrixUserID = matrixIdentifiers.firstObject; - // check if already known as a Matrix user - MXUser* mxUser = [[ContactManager sharedManager].mxSession userWithUserId:matrixUserID]; - - // check if the mxUser is known - // if it is not known, the presence cannot be retrieved - if (mxUser) + // Consider here all sessions reported into contact manager + NSArray* mxSessions = [ContactManager sharedManager].mxSessions; + for (MXSession *mxSession in mxSessions) { - [self refreshPresenceUserRing:mxUser.presence]; - // we know that this user is a matrix one - self.matrixUserIconView.hidden = NO; + MXUser *mxUser = [mxSession userWithUserId:matrixUserID]; + if (mxUser) + { + [self refreshPresenceUserRing:mxUser.presence]; + break; + } } + + // we know that this user is a matrix one + self.matrixUserIconView.hidden = NO; } } diff --git a/matrixConsole/ViewController/ContactsViewController.m b/matrixConsole/ViewController/ContactsViewController.m index b575652f8..eea76d437 100644 --- a/matrixConsole/ViewController/ContactsViewController.m +++ b/matrixConsole/ViewController/ContactsViewController.m @@ -42,7 +42,7 @@ NSString *const kInvitationMessage = @"I'd like to chat with you with matrix. Pl BOOL displayMatrixUsers; // screenshot of the local contacts - NSMutableArray* localContacts; + NSArray* localContacts; SectionedContacts* sectionedLocalContacts; // screenshot of the matrix users @@ -83,7 +83,8 @@ NSString *const kInvitationMessage = @"I'd like to chat with you with matrix. Pl UIBarButtonItem *searchButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(search:)]; self.navigationItem.rightBarButtonItems = @[searchButton]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactsRefresh:) name:kContactManagerContactsListRefreshNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactsRefresh:) name:kContactManagerDidUpdateContactsNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactsRefresh:) name:kContactManagerDidUpdateContactMatrixIDsNotification object:nil]; // Set rageShake handler self.rageShakeManager = [RageShakeManager sharedManager]; @@ -555,6 +556,12 @@ NSString *const kInvitationMessage = @"I'd like to chat with you with matrix. Pl } } +- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath +{ + // Release here resources, and restore reusable cells + ((ContactTableCell*)cell).contact = nil; +} + #pragma mark - Actions - (void)onContactsRefresh:(NSNotification *)notif diff --git a/matrixConsole/ViewController/SettingsViewController.m b/matrixConsole/ViewController/SettingsViewController.m index df3adee90..3c04c4981 100644 --- a/matrixConsole/ViewController/SettingsViewController.m +++ b/matrixConsole/ViewController/SettingsViewController.m @@ -198,7 +198,7 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl countryCode = selectedCountryCode; [[ContactManager sharedManager] internationalizePhoneNumbers:countryCode]; - [[ContactManager sharedManager] fullRefresh]; + [[ContactManager sharedManager] loadContacts]; } countryCode = [_settings phonebookCountryCode];