diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9091840e6..3fbf71646 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -167,7 +167,8 @@ "contacts_address_book_no_contact" = "No local contacts"; "contacts_address_book_permission_required" = "Permission required to access local contacts"; "contacts_address_book_permission_denied" = "You didn't allow Riot to access your local contacts"; -"contacts_matrix_users_section" = "KNOWN CONTACTS"; +"contacts_user_directory_section" = "USER DIRECTORY"; +"contacts_user_directory_offline_section" = "USER DIRECTORY (offline)"; // Chat participants "room_participants_title" = "Participants"; diff --git a/Riot/Model/Contact/ContactsDataSource.h b/Riot/Model/Contact/ContactsDataSource.h index b63f74514..4e8005f0c 100644 --- a/Riot/Model/Contact/ContactsDataSource.h +++ b/Riot/Model/Contact/ContactsDataSource.h @@ -16,6 +16,20 @@ #import +/** + The state of the users search from the homeserver user directory. + */ +typedef enum : NSUInteger +{ + ContactsDataSourceUserDirectoryStateLoading, + ContactsDataSourceUserDirectoryStateLoadedButLimited, + ContactsDataSourceUserDirectoryStateLoaded, + // The search is based on local known matrix contacts + ContactsDataSourceUserDirectoryStateOfflineLoading, + ContactsDataSourceUserDirectoryStateOfflineLoaded +} ContactsDataSourceUserDirectoryState; + + /** 'ContactsDataSource' is a base class to handle contacts in Riot. */ @@ -145,4 +159,9 @@ */ @property (nonatomic, readonly) MXKContact *searchInputContact; +/** + The state of the users search from the homeserver user directory. + */ +@property (nonatomic, readonly) ContactsDataSourceUserDirectoryState userDirectoryState; + @end diff --git a/Riot/Model/Contact/ContactsDataSource.m b/Riot/Model/Contact/ContactsDataSource.m index 5c880b241..bab141466 100644 --- a/Riot/Model/Contact/ContactsDataSource.m +++ b/Riot/Model/Contact/ContactsDataSource.m @@ -33,6 +33,9 @@ NSString *searchProcessingText; NSMutableArray *searchProcessingLocalContacts; NSMutableArray *searchProcessingMatrixContacts; + + // The current request to the homeserver user directory + MXHTTPOperation *hsUserDirectoryOperation; BOOL forceSearchResultRefresh; @@ -115,6 +118,9 @@ localContactsCheckboxContainer = nil; localContactsCheckbox = nil; + + [hsUserDirectoryOperation cancel]; + hsUserDirectoryOperation = nil; [super destroy]; } @@ -145,6 +151,13 @@ } - (void)searchWithPattern:(NSString *)searchText forceReset:(BOOL)forceRefresh +{ + // If possible, always start a new search by asking the homeserver user directory + BOOL hsUserDirectory = (self.mxSession.state != MXSessionStateHomeserverNotReachable); + [self searchWithPattern:searchText forceReset:forceRefresh hsUserDirectory:hsUserDirectory]; +} + +- (void)searchWithPattern:(NSString *)searchText forceReset:(BOOL)forceRefresh hsUserDirectory:(BOOL)hsUserDirectory { // Update search results. searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; @@ -161,11 +174,59 @@ shrinkedSectionsBitMask = 0; } } - else if (forceRefresh || !searchProcessingText.length || [searchText hasPrefix:searchProcessingText] == NO) + else if (forceRefresh || ![searchText isEqualToString:searchProcessingText]) { // Prepare on the main thread the arrays used to initialize the search on the processing queue. unfilteredLocalContacts = [self unfilteredLocalContactsArray]; - unfilteredMatrixContacts = [self unfilteredMatrixContactsArray]; + if (!hsUserDirectory) + { + _userDirectoryState = ContactsDataSourceUserDirectoryStateOfflineLoading; + unfilteredMatrixContacts = [self unfilteredMatrixContactsArray]; + } + else if (![searchText isEqualToString:searchProcessingText]) + { + _userDirectoryState = ContactsDataSourceUserDirectoryStateLoading; + + // Make a search on the homeserver user directory + [filteredMatrixContacts removeAllObjects]; + filteredMatrixContacts = nil; + + // Cancel previous operation + if (hsUserDirectoryOperation) + { + [hsUserDirectoryOperation cancel]; + hsUserDirectoryOperation = nil; + } + + hsUserDirectoryOperation = [self.mxSession.matrixRestClient searchUsers:searchText limit:100 success:^(MXUserSearchResponse *userSearchResponse) { + + filteredMatrixContacts = [NSMutableArray arrayWithCapacity:userSearchResponse.results.count]; + + // Keep the response order as the hs ordered users by relevance + for (MXUser *mxUser in userSearchResponse.results) + { + MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:mxUser.displayname andMatrixID:mxUser.userId]; + [filteredMatrixContacts addObject:contact]; + } + + hsUserDirectoryOperation = nil; + + _userDirectoryState = userSearchResponse.limited ? ContactsDataSourceUserDirectoryStateLoadedButLimited : ContactsDataSourceUserDirectoryStateLoaded; + + // And inform the delegate about the update + [self.delegate dataSource:self didCellChange:nil]; + + } failure:^(NSError *error) { + + // Ignore connection cancellation error + if ((![error.domain isEqualToString:NSURLErrorDomain] || error.code != NSURLErrorCancelled)) + { + // But for other errors, launch a local search + NSLog(@"[ContactsDataSource] [MXRestClient searchUsers] returns an error. Do a search on local known contacts"); + [self searchWithPattern:searchText forceReset:forceRefresh hsUserDirectory:NO]; + } + }]; + } // Disclose the sections shrinkedSectionsBitMask = 0; @@ -239,7 +300,12 @@ // Update the filtered contacts. currentSearchText = searchProcessingText; filteredLocalContacts = searchProcessingLocalContacts; - filteredMatrixContacts = searchProcessingMatrixContacts; + + if (!hsUserDirectory) + { + filteredMatrixContacts = searchProcessingMatrixContacts; + _userDirectoryState = ContactsDataSourceUserDirectoryStateOfflineLoaded; + } if (!self.forceMatrixIdInDisplayName) { @@ -643,7 +709,17 @@ } else //if (section == filteredMatrixContactsSection) { - title = NSLocalizedStringFromTable(@"contacts_matrix_users_section", @"Vector", nil); + switch (_userDirectoryState) + { + case ContactsDataSourceUserDirectoryStateOfflineLoading: + case ContactsDataSourceUserDirectoryStateOfflineLoaded: + title = NSLocalizedStringFromTable(@"contacts_user_directory_offline_section", @"Vector", nil); + break; + + default: + title = NSLocalizedStringFromTable(@"contacts_user_directory_section", @"Vector", nil); + break; + } if (currentSearchText.length) {