diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index c767c6dd3..ad472c832 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -422,6 +422,9 @@ F083BEA21E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BD1B1E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.m */; }; F083BEA31E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.xib in Resources */ = {isa = PBXBuildFile; fileRef = F083BD1C1E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.xib */; }; F083BEA51E70356E00A9B29C /* RiotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BB041E7005FD00A9B29C /* RiotTests.m */; }; + F0A4A1671EF7CB66003630DB /* members_list_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1641EF7CB66003630DB /* members_list_icon.png */; }; + F0A4A1681EF7CB66003630DB /* members_list_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1651EF7CB66003630DB /* members_list_icon@2x.png */; }; + F0A4A1691EF7CB66003630DB /* members_list_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1661EF7CB66003630DB /* members_list_icon@3x.png */; }; F0D869EB1EC455A100BB0A2B /* create_direct_chat.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869E81EC455A100BB0A2B /* create_direct_chat.png */; }; F0D869EC1EC455A100BB0A2B /* create_direct_chat@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869E91EC455A100BB0A2B /* create_direct_chat@2x.png */; }; F0D869ED1EC455A100BB0A2B /* create_direct_chat@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869EA1EC455A100BB0A2B /* create_direct_chat@3x.png */; }; @@ -1006,6 +1009,9 @@ F083BD1C1E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TableViewCellWithPhoneNumberTextField.xib; sourceTree = ""; }; F094A9A21B78D8F000B1FBBF /* Riot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Riot.app; sourceTree = BUILT_PRODUCTS_DIR; }; F094A9BE1B78D8F000B1FBBF /* RiotTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RiotTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F0A4A1641EF7CB66003630DB /* members_list_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = members_list_icon.png; sourceTree = ""; }; + F0A4A1651EF7CB66003630DB /* members_list_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "members_list_icon@2x.png"; sourceTree = ""; }; + F0A4A1661EF7CB66003630DB /* members_list_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "members_list_icon@3x.png"; sourceTree = ""; }; F0D869E81EC455A100BB0A2B /* create_direct_chat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = create_direct_chat.png; sourceTree = ""; }; F0D869E91EC455A100BB0A2B /* create_direct_chat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "create_direct_chat@2x.png"; sourceTree = ""; }; F0D869EA1EC455A100BB0A2B /* create_direct_chat@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "create_direct_chat@3x.png"; sourceTree = ""; }; @@ -1156,6 +1162,9 @@ F083BB151E7009EC00A9B29C /* Images */ = { isa = PBXGroup; children = ( + F0A4A1641EF7CB66003630DB /* members_list_icon.png */, + F0A4A1651EF7CB66003630DB /* members_list_icon@2x.png */, + F0A4A1661EF7CB66003630DB /* members_list_icon@3x.png */, F0614A101EDEE65000F5DC9A /* cancel.png */, F0614A111EDEE65000F5DC9A /* cancel@2x.png */, F0614A121EDEE65000F5DC9A /* cancel@3x.png */, @@ -2130,6 +2139,7 @@ F083BD891E7009ED00A9B29C /* file_music_icon.png in Resources */, F083BDD11E7009ED00A9B29C /* settings_icon.png in Resources */, F083BD811E7009ED00A9B29C /* favourite@2x.png in Resources */, + F0A4A1681EF7CB66003630DB /* members_list_icon@2x.png in Resources */, F083BD9F1E7009ED00A9B29C /* mod_icon@2x.png in Resources */, F083BD801E7009ED00A9B29C /* favourite.png in Resources */, F083BD4B1E7009ED00A9B29C /* camera_capture.png in Resources */, @@ -2266,6 +2276,7 @@ F083BD631E7009ED00A9B29C /* direct_icon@2x.png in Resources */, F083BE991E7009ED00A9B29C /* MessagesSearchResultTextMsgBubbleCell.xib in Resources */, F083BD711E7009ED00A9B29C /* e2e_unencrypted.png in Resources */, + F0A4A1671EF7CB66003630DB /* members_list_icon.png in Resources */, F083BE731E7009ED00A9B29C /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, F083BD251E7009ED00A9B29C /* admin_icon.png in Resources */, F083BDBE1E7009ED00A9B29C /* riot_icon@3x.png in Resources */, @@ -2328,6 +2339,7 @@ F083BDE01E7009ED00A9B29C /* video_icon.png in Resources */, F083BD601E7009ED00A9B29C /* details_icon@2x.png in Resources */, F083BE5F1E7009ED00A9B29C /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib in Resources */, + F0A4A1691EF7CB66003630DB /* members_list_icon@3x.png in Resources */, F083BE8D1E7009ED00A9B29C /* PreviewRoomTitleView.xib in Resources */, F083BD561E7009ED00A9B29C /* camera_video_capture.png in Resources */, F083BD651E7009ED00A9B29C /* directChatOff.png in Resources */, diff --git a/Riot/Assets/Images/members_list_icon.png b/Riot/Assets/Images/members_list_icon.png new file mode 100644 index 000000000..eb23ecd65 Binary files /dev/null and b/Riot/Assets/Images/members_list_icon.png differ diff --git a/Riot/Assets/Images/members_list_icon@2x.png b/Riot/Assets/Images/members_list_icon@2x.png new file mode 100644 index 000000000..8eb7a46ca Binary files /dev/null and b/Riot/Assets/Images/members_list_icon@2x.png differ diff --git a/Riot/Assets/Images/members_list_icon@3x.png b/Riot/Assets/Images/members_list_icon@3x.png new file mode 100644 index 000000000..ec24ed814 Binary files /dev/null and b/Riot/Assets/Images/members_list_icon@3x.png differ diff --git a/Riot/Base.lproj/Main.storyboard b/Riot/Base.lproj/Main.storyboard index d4a2d4641..abe200650 100644 --- a/Riot/Base.lproj/Main.storyboard +++ b/Riot/Base.lproj/Main.storyboard @@ -45,6 +45,7 @@ + @@ -524,6 +525,24 @@ + + + + + + + + + + + + + + + + + + @@ -558,9 +577,9 @@ - + + - - + diff --git a/Riot/ViewController/RoomParticipantsViewController.m b/Riot/ViewController/RoomParticipantsViewController.m index 0fd839f48..9f762fbd0 100644 --- a/Riot/ViewController/RoomParticipantsViewController.m +++ b/Riot/ViewController/RoomParticipantsViewController.m @@ -601,7 +601,7 @@ // Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user. contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"]; - // List all the participants by their matrix user id, or a room 3pid invite token to ignore them during the contacts search. + // List all the participants matrix user id to ignore them during the contacts search. for (Contact *contact in actualParticipants) { [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxMember.userId]; @@ -612,10 +612,6 @@ { [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxMember.userId]; } - else if (contact.mxThirdPartyInvite) - { - [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:contact.mxThirdPartyInvite.token]; - } } if (userParticipant) { @@ -905,7 +901,7 @@ - (void)pushViewController:(UIViewController*)viewController { // Check whether the view controller is displayed inside a segmented one. - if (self.parentViewController) + if (self.parentViewController.navigationController) { // Hide back button title self.parentViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; diff --git a/Riot/ViewController/RoomViewController.h b/Riot/ViewController/RoomViewController.h index 45fd0f0bd..ac4c2ac11 100644 --- a/Riot/ViewController/RoomViewController.h +++ b/Riot/ViewController/RoomViewController.h @@ -23,9 +23,11 @@ #import "RoomParticipantsViewController.h" +#import "ContactsTableViewController.h" + #import "UIViewController+RiotSearch.h" -@interface RoomViewController : MXKRoomViewController +@interface RoomViewController : MXKRoomViewController // The expanded header @property (weak, nonatomic) IBOutlet UIView *expandedHeaderContainer; diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index 001c888b1..77460e2c0 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -906,7 +906,7 @@ } failure:^(NSError *error) { - NSLog(@"[Vector RoomVC] Join roomAlias (%@) failed", roomAlias); + NSLog(@"[RoomVC] Join roomAlias (%@) failed", roomAlias); //Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -1007,7 +1007,7 @@ if (previewHeader || (self.expandedHeaderContainer.isHidden == NO)) { // Here [destroy] is called before [viewWillDisappear:] - NSLog(@"[Vector RoomVC] destroyed whereas it is still visible"); + NSLog(@"[RoomVC] destroyed whereas it is still visible"); [previewHeader removeFromSuperview]; previewHeader = nil; @@ -1198,7 +1198,7 @@ // - if the event details view is displayed if (isVisible && (isSizeTransitionInProgress == YES || !self.roomDataSource || !self.roomDataSource.isLive || (self.roomDataSource.room.state.membership != MXMembershipJoin) || !self.splitViewController || encryptionInfoView.superview || eventDetailsView.superview)) { - NSLog(@"[Vector RoomVC] Show expanded header ignored"); + NSLog(@"[RoomVC] Show expanded header ignored"); return; } @@ -1279,7 +1279,7 @@ // or if the view controller is not embedded inside a split view controller yet. if (isVisible && (isSizeTransitionInProgress == YES || !self.splitViewController)) { - NSLog(@"[Vector RoomVC] Show preview header ignored"); + NSLog(@"[RoomVC] Show preview header ignored"); return; } @@ -1984,7 +1984,7 @@ __strong __typeof(weakSelf)strongSelf = weakSelf; [strongSelf stopActivityIndicator]; - NSLog(@"[Vector RoomVC] Redact event (%@) failed", selectedEvent.eventId); + NSLog(@"[RoomVC] Redact event (%@) failed", selectedEvent.eventId); //Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -2071,7 +2071,7 @@ __strong __typeof(weakSelf)strongSelf = weakSelf; [strongSelf stopActivityIndicator]; - NSLog(@"[Vector RoomVC] Ignore user (%@) failed", selectedEvent.sender); + NSLog(@"[RoomVC] Ignore user (%@) failed", selectedEvent.sender); //Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -2093,7 +2093,7 @@ __strong __typeof(weakSelf)strongSelf = weakSelf; [strongSelf stopActivityIndicator]; - NSLog(@"[Vector RoomVC] Report event (%@) failed", selectedEvent.eventId); + NSLog(@"[RoomVC] Report event (%@) failed", selectedEvent.eventId); //Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -2351,6 +2351,46 @@ unknownDevices = nil; } } + else if ([[segue identifier] isEqualToString:@"showContactPicker"]) + { + ContactsTableViewController *contactsPickerViewController = (ContactsTableViewController*)pushedViewController; + + // Set delegate to handle selected contact + contactsPickerViewController.contactsTableViewControllerDelegate = self; + + // Prepare its data source + ContactsDataSource *contactsDataSource = [[ContactsDataSource alloc] init]; + contactsDataSource.areSectionsShrinkable = YES; + contactsDataSource.displaySearchInputInContactsList = YES; + contactsDataSource.forceMatrixIdInDisplayName = YES; + // Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user. + contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"]; + + // List all the participants matrix user id to ignore them during the contacts search. + MXSession* session = self.roomDataSource.mxSession; + NSString* roomId = self.roomDataSource.roomId; + MXRoom *room = [session roomWithRoomId:roomId]; + if (room) + { + NSArray *members = [room.state membersWithoutConferenceUser]; + + for (MXRoomMember *mxMember in members) + { + // Check his status + if (mxMember.membership == MXMembershipJoin || mxMember.membership == MXMembershipInvite) + { + // Create the contact related to this member + MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:mxMember.displayname andMatrixID:mxMember.userId]; + [contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:mxMember.userId]; + } + } + } + + [contactsPickerViewController showSearch:YES]; + contactsPickerViewController.searchBar.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil); + + [contactsPickerViewController displayList:contactsDataSource]; + } // Hide back button title self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; @@ -2732,6 +2772,11 @@ selectedRoomDetailsIndex = 0; [self performSegueWithIdentifier:@"showRoomDetails" sender:self]; } + else if (tappedView == titleView.addParticipantMask) + { + // Open contact picker + [self performSegueWithIdentifier:@"showContactPicker" sender:self]; + } else if (tappedView == previewHeader.rightButton) { // 'Join' button has been pressed @@ -2820,7 +2865,7 @@ } failure:^(NSError *error) { [self stopActivityIndicator]; - NSLog(@"[Vector RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.state.roomId); + NSLog(@"[RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.state.roomId); }]; } @@ -3021,7 +3066,7 @@ { [roomActivitiesView displayOngoingConferenceCall:^(BOOL video) { - NSLog(@"[Vector RoomVC] onOngoingConferenceCallPressed"); + NSLog(@"[RoomVC] onOngoingConferenceCallPressed"); // Make sure there is not yet a call if (![customizedRoomDataSource.mxSession.callManager callInRoom:customizedRoomDataSource.roomId]) @@ -3621,5 +3666,118 @@ } } +#pragma mark - ContactsTableViewControllerDelegate + +- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact +{ + __weak typeof(self) weakSelf = self; + + if (currentAlert) + { + [currentAlert dismiss:NO]; + currentAlert = nil; + } + + // Invite ? + NSString *promptMsg = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_participants_invite_prompt_msg", @"Vector", nil), contact.displayName]; + currentAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"room_participants_invite_prompt_title", @"Vector", nil) + message:promptMsg + style:MXKAlertStyleAlert]; + + currentAlert.cancelButtonIndex = [currentAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:MXKAlertActionStyleCancel + handler:^(MXKAlert *alert) { + + __strong __typeof(weakSelf)strongSelf = weakSelf; + strongSelf->currentAlert = nil; + + }]; + + [currentAlert addActionWithTitle:NSLocalizedStringFromTable(@"invite", @"Vector", nil) + style:MXKAlertActionStyleDefault + handler:^(MXKAlert *alert) { + + __strong __typeof(weakSelf)strongSelf = weakSelf; + strongSelf->currentAlert = nil; + + MXSession* session = strongSelf.roomDataSource.mxSession; + NSString* roomId = strongSelf.roomDataSource.roomId; + MXRoom *room = [session roomWithRoomId:roomId]; + + NSArray *identifiers = contact.matrixIdentifiers; + NSString *participantId; + + if (identifiers.count) + { + participantId = identifiers.firstObject; + + // Invite this user if a room is defined + [room inviteUser:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + else + { + if (contact.emailAddresses.count) + { + // This is a local contact, consider the first email by default. + // TODO: Prompt the user to select the right email. + MXKEmail *email = contact.emailAddresses.firstObject; + participantId = email.emailAddress; + } + else + { + // This is the text filled by the user. + participantId = contact.displayName; + } + + // Is it an email or a Matrix user ID? + if ([MXTools isEmailAddress:participantId]) + { + [room inviteUserByEmail:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite be email %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + else //if ([MXTools isMatrixUserIdentifier:participantId]) + { + [room inviteUser:participantId success:^{ + + // Refresh display by removing the contacts picker + [contactsTableViewController withdrawViewControllerAnimated:YES completion:nil]; + + } failure:^(NSError *error) { + + NSLog(@"[RoomVC] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + } + + }]; + + currentAlert.mxkAccessibilityIdentifier = @"RoomVCInviteAlert"; + [currentAlert showInViewController:self]; +} + @end diff --git a/Riot/Views/RoomTitle/ExpandedRoomTitleView.xib b/Riot/Views/RoomTitle/ExpandedRoomTitleView.xib index 200d52f66..78005181b 100644 --- a/Riot/Views/RoomTitle/ExpandedRoomTitleView.xib +++ b/Riot/Views/RoomTitle/ExpandedRoomTitleView.xib @@ -1,11 +1,12 @@ - - + + - + + @@ -47,12 +48,12 @@ - - + + - - + + @@ -67,9 +68,28 @@ - + + + + + + + + + + + + + + + + + + + + @@ -77,15 +97,18 @@ + - + + - + + @@ -97,16 +120,17 @@ + - + @@ -119,6 +143,7 @@ - + + diff --git a/Riot/Views/RoomTitle/RoomTitleView.h b/Riot/Views/RoomTitle/RoomTitleView.h index 36d9ee86f..23b198f5e 100644 --- a/Riot/Views/RoomTitle/RoomTitleView.h +++ b/Riot/Views/RoomTitle/RoomTitleView.h @@ -36,6 +36,7 @@ @property (weak, nonatomic) IBOutlet UIView *titleMask; @property (weak, nonatomic) IBOutlet UIView *roomDetailsMask; +@property (weak, nonatomic) IBOutlet UIView *addParticipantMask; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *displayNameCenterXConstraint; @property (weak, nonatomic) IBOutlet UIImageView *roomDetailsIconImageView; @@ -54,4 +55,4 @@ */ - (void)reportTapGesture:(UITapGestureRecognizer*)tapGestureRecognizer; -@end \ No newline at end of file +@end diff --git a/Riot/Views/RoomTitle/RoomTitleView.m b/Riot/Views/RoomTitle/RoomTitleView.m index cbe4b0278..2240da8e5 100644 --- a/Riot/Views/RoomTitle/RoomTitleView.m +++ b/Riot/Views/RoomTitle/RoomTitleView.m @@ -59,6 +59,16 @@ [self.roomDetailsMask addGestureRecognizer:tap]; self.roomDetailsMask.userInteractionEnabled = YES; } + + if (_addParticipantMask) + { + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reportTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.addParticipantMask addGestureRecognizer:tap]; + self.addParticipantMask.userInteractionEnabled = YES; + } } - (void)layoutSubviews