From 44f5d55bd7debc016ae5ba35f44e62dc73143e8e Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 14 Apr 2022 17:41:16 +0200 Subject: [PATCH 1/4] =?UTF-8?q?[Left=20panel]=20add=20an=20option=20"Invit?= =?UTF-8?q?er=20=C3=A0=20rejoindre=20Tchap"=20(#449)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Btchap/Config/BuildSettings.swift | 2 + Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Assets/fr.lproj/Vector.strings | 2 +- Riot/Modules/Application/AppCoordinator.swift | 3 + .../SideMenu/SideMenuCoordinator.swift | 6 +- Riot/Modules/SideMenu/SideMenuViewModel.swift | 6 +- .../SplitView/SplitViewCoordinator.swift | 4 + .../SplitView/SplitViewCoordinatorType.swift | 4 + Riot/Modules/TabBar/TabBarCoordinator.swift | 138 ++++++++++++++++++ .../TabBar/TabBarCoordinatorType.swift | 4 + RiotNSE/BuildSettings.swift | 2 + RiotShareExtension/BuildSettings.swift | 2 + Tchap/Config/BuildSettings.swift | 2 + changelog.d/449.change | 1 + 14 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 changelog.d/449.change diff --git a/Btchap/Config/BuildSettings.swift b/Btchap/Config/BuildSettings.swift index 827de4bd21..a6ab015c6b 100644 --- a/Btchap/Config/BuildSettings.swift +++ b/Btchap/Config/BuildSettings.swift @@ -191,7 +191,9 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + // MARK: - Side Menu static let enableSideMenu: Bool = true + static let sideMenuShowInviteFriends: Bool = true /// Whether to read the `io.element.functional_members` state event and exclude any service members when computing a room's name and avatar. static let supportFunctionalMembers: Bool = true diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 08e82c15ed..86d1b9aac3 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1829,7 +1829,7 @@ Tap the + to start adding people."; // Mark: - Side menu "side_menu_reveal_action_accessibility_label" = "Left panel"; -"side_menu_action_invite_friends" = "Invite friends"; +"side_menu_action_invite_friends" = "Invite to join Tchap"; "side_menu_action_settings" = "Settings"; "side_menu_action_help" = "Help"; "side_menu_action_feedback" = "Feedback"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index e0ec2d1687..555f527c71 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -1421,7 +1421,7 @@ "side_menu_action_feedback" = "Remarques"; "side_menu_action_help" = "Aide"; "side_menu_action_settings" = "Paramètres"; -"side_menu_action_invite_friends" = "Inviter des amis"; +"side_menu_action_invite_friends" = "Inviter à rejoindre Tchap"; // Mark: - Side menu diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index 5c06d9a67b..96a23cef1a 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -335,6 +335,9 @@ extension AppCoordinator: SplitViewCoordinatorDelegate { // MARK: - SideMenuCoordinatorDelegate extension AppCoordinator: SideMenuCoordinatorDelegate { func sideMenuCoordinator(_ coordinator: SideMenuCoordinatorType, didTapMenuItem menuItem: SideMenuItem, fromSourceView sourceView: UIView) { + if menuItem == .inviteFriends { + self.splitViewCoordinator?.presentInvitePeople() + } } } diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index f79cde3910..ce2667c370 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -280,7 +280,11 @@ extension SideMenuCoordinator: SideMenuViewModelCoordinatorDelegate { switch menuItem { case .inviteFriends: - self.showInviteFriends(from: sourceView) + // Tchap: Redirect to invite people + self.sideMenuNavigationViewController.dismiss(animated: true) { + self.delegate?.sideMenuCoordinator(self, didTapMenuItem: menuItem, fromSourceView: sourceView) + } + break case .settings: self.showSettings() case .help: diff --git a/Riot/Modules/SideMenu/SideMenuViewModel.swift b/Riot/Modules/SideMenu/SideMenuViewModel.swift index 820884bd5b..0e24abcb14 100644 --- a/Riot/Modules/SideMenu/SideMenuViewModel.swift +++ b/Riot/Modules/SideMenu/SideMenuViewModel.swift @@ -99,9 +99,9 @@ final class SideMenuViewModel: SideMenuViewModelType { var sideMenuItems: [SideMenuItem] = [] -// if BuildSettings.sideMenuShowInviteFriends { -// sideMenuItems += [.inviteFriends] -// } + if BuildSettings.sideMenuShowInviteFriends { + sideMenuItems += [.inviteFriends] + } sideMenuItems += [ .settings, diff --git a/Riot/Modules/SplitView/SplitViewCoordinator.swift b/Riot/Modules/SplitView/SplitViewCoordinator.swift index 2157cbd311..d1ce05204e 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinator.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinator.swift @@ -144,6 +144,10 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType { self.tabBarCoordinator?.popToHome(animated: animated, completion: completion) } + func presentInvitePeople() { + self.tabBarCoordinator?.presentInvitePeople() + } + // MARK: - Private methods private func createPlaceholderDetailsViewController() -> UIViewController { diff --git a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift index 4fd2cfef62..337888c19d 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift @@ -36,4 +36,8 @@ protocol SplitViewCoordinatorType: Coordinator, Presentable { // TODO: Do not expose publicly this method /// Remove detail screens and display placeholder if needed func resetDetails(animated: Bool) + + // Tchap: redirect to invite people alert + /// Present invite people alert (with textField) + func presentInvitePeople() } diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index 684092ed93..10c21a3020 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -53,6 +53,11 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { return self.navigationRouter.modules.last is MasterTabBarController } + // Tchap: Add invite service for user invitation + private var inviteService: InviteServiceType? + private var errorPresenter: ErrorPresenter? + private weak var currentAlertController: UIAlertController? + // MARK: Public // Must be used only internally @@ -110,6 +115,11 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { if MXKAccountManager.shared().accounts.isEmpty { self.showWelcome() } + + self.errorPresenter = AlertErrorPresenter(viewControllerPresenter: masterTabBarController) + + guard let session = self.currentMatrixSession else { return } + self.inviteService = InviteService(session: session) } func toPresentable() -> UIViewController { @@ -181,6 +191,12 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { } } + func presentInvitePeople() { + promptUserToFillAnEmailToInvite { [weak self] email in + self?.sendEmailInvite(to: email) + } + } + // MARK: - SplitViewMasterPresentable var selectedNavigationRouter: NavigationRouterType? { @@ -705,6 +721,128 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { } } +// Tchap: Manage e-mail invitation +extension TabBarCoordinator { + private func promptUserToFillAnEmailToInvite(completion: @escaping ((String) -> Void)) { + currentAlertController?.dismiss(animated: false) + + let alertController = UIAlertController(title: TchapL10n.contactsInviteByEmailTitle, + message: TchapL10n.contactsInviteByEmailMessage, + preferredStyle: .alert) + + // Add textField + alertController.addTextField(configurationHandler: { textField in + textField.isSecureTextEntry = false + textField.placeholder = nil + textField.keyboardType = .emailAddress + }) + + // Cancel action + let cancelAction = UIAlertAction(title: TchapL10n.actionCancel, + style: .cancel) { [weak self] _ in + self?.currentAlertController = nil + } + alertController.addAction(cancelAction) + + // Invite action + let inviteAction = UIAlertAction(title: TchapL10n.actionInvite, + style: .default) { [weak self] _ in + guard let currentAlertController = self?.currentAlertController, + let email = currentAlertController.textFields?.first?.text?.lowercased() else { + return // FIXME: Verify if dismiss should be needed in this case + } + + self?.currentAlertController = nil + + if MXTools.isEmailAddress(email) { + completion(email) + } else { + self?.currentAlertController?.dismiss(animated: false) + let errorAlertController = UIAlertController(title: TchapL10n.authenticationErrorInvalidEmail, + message: nil, + preferredStyle: .alert) + let okAction = UIAlertAction(title: VectorL10n.ok, + style: .default) { [weak self] _ in + self?.currentAlertController = nil + } + errorAlertController.addAction(okAction) + errorAlertController.mxk_setAccessibilityIdentifier("ContactsVCInviteByEmailError") + self?.currentAlertController = errorAlertController + self?.masterTabBarController.present(errorAlertController, animated: true) + } + } + alertController.addAction(inviteAction) + alertController.mxk_setAccessibilityIdentifier("ContactsVCInviteByEmailDialog") + + self.currentAlertController = alertController + + masterTabBarController.present(alertController, animated: true) + } + + private func sendEmailInvite(to email: String) { + guard let inviteService = self.inviteService else { return } + + self.activityIndicatorPresenter.presentActivityIndicator(on: masterTabBarController.view, animated: true) + inviteService.sendEmailInvite(to: email) { [weak self] (response) in + guard let sself = self else { + return + } + + sself.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true) + switch response { + case .success(let result): + var message: String + var discoveredUserID: String? + switch result { + case .inviteHasBeenSent(roomID: _): + message = TchapL10n.inviteSendingSucceeded + case .inviteAlreadySent(roomID: _): + message = TchapL10n.inviteAlreadySentByEmail(email) + case .inviteIgnoredForDiscoveredUser(userID: let userID): + discoveredUserID = userID + message = TchapL10n.inviteNotSentForDiscoveredUser + case .inviteIgnoredForUnauthorizedEmail: + message = TchapL10n.inviteNotSentForUnauthorizedEmail(email) + } + + sself.currentAlertController?.dismiss(animated: false) + + let alert = UIAlertController(title: TchapL10n.inviteInformationTitle, message: message, preferredStyle: .alert) + + let okTitle = Bundle.mxk_localizedString(forKey: "ok") + let okAction = UIAlertAction(title: okTitle, style: .default, handler: { action in + if let userID = discoveredUserID { + // Open the discussion + sself.startDiscussion(with: userID) + } + }) + alert.addAction(okAction) + sself.currentAlertController = alert + + sself.masterTabBarController.present(alert, animated: true, completion: nil) + case .failure(let error): + let errorPresentable = sself.inviteErrorPresentable(from: error) + sself.errorPresenter?.present(errorPresentable: errorPresentable, animated: true) + } + } + } + + private func inviteErrorPresentable(from error: Error) -> ErrorPresentable { + let errorTitle = TchapL10n.inviteSendingFailedTitle + let errorMessage: String + + let nsError = error as NSError + + if let message = nsError.userInfo[NSLocalizedDescriptionKey] as? String { + errorMessage = message + } else { + errorMessage = TchapL10n.errorMessageDefault + } + + return ErrorPresentableImpl(title: errorTitle, message: errorMessage) + } +} + // MARK: - MasterTabBarControllerDelegate extension TabBarCoordinator: MasterTabBarControllerDelegate { diff --git a/Riot/Modules/TabBar/TabBarCoordinatorType.swift b/Riot/Modules/TabBar/TabBarCoordinatorType.swift index 8d916606af..e749028a0e 100644 --- a/Riot/Modules/TabBar/TabBarCoordinatorType.swift +++ b/Riot/Modules/TabBar/TabBarCoordinatorType.swift @@ -37,4 +37,8 @@ protocol TabBarCoordinatorType: Coordinator, SplitViewMasterPresentable { // TODO: Remove this method, this implementation detail should not be exposed // Release the current selected item (room/contact/group...). func releaseSelectedItems() + + // Tchap: redirect to invite people alert + /// Present invite people alert (with textField) + func presentInvitePeople() } diff --git a/RiotNSE/BuildSettings.swift b/RiotNSE/BuildSettings.swift index fb07f880b4..fb1dfe86c3 100644 --- a/RiotNSE/BuildSettings.swift +++ b/RiotNSE/BuildSettings.swift @@ -220,7 +220,9 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + // MARK: - Side Menu static let enableSideMenu: Bool = true + static let sideMenuShowInviteFriends: Bool = true /// Whether to read the `io.element.functional_members` state event and exclude any service members when computing a room's name and avatar. static let supportFunctionalMembers: Bool = true diff --git a/RiotShareExtension/BuildSettings.swift b/RiotShareExtension/BuildSettings.swift index fb07f880b4..fb1dfe86c3 100644 --- a/RiotShareExtension/BuildSettings.swift +++ b/RiotShareExtension/BuildSettings.swift @@ -220,7 +220,9 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + // MARK: - Side Menu static let enableSideMenu: Bool = true + static let sideMenuShowInviteFriends: Bool = true /// Whether to read the `io.element.functional_members` state event and exclude any service members when computing a room's name and avatar. static let supportFunctionalMembers: Bool = true diff --git a/Tchap/Config/BuildSettings.swift b/Tchap/Config/BuildSettings.swift index fb07f880b4..fb1dfe86c3 100644 --- a/Tchap/Config/BuildSettings.swift +++ b/Tchap/Config/BuildSettings.swift @@ -220,7 +220,9 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + // MARK: - Side Menu static let enableSideMenu: Bool = true + static let sideMenuShowInviteFriends: Bool = true /// Whether to read the `io.element.functional_members` state event and exclude any service members when computing a room's name and avatar. static let supportFunctionalMembers: Bool = true diff --git a/changelog.d/449.change b/changelog.d/449.change new file mode 100644 index 0000000000..1475a546e6 --- /dev/null +++ b/changelog.d/449.change @@ -0,0 +1 @@ +[Left panel] add an option "Inviter à rejoindre Tchap" From eacd68489dd52ef3c9250eebb0b4084ff20c8623 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 15 Apr 2022 17:06:39 +0200 Subject: [PATCH 2/4] Fix comments --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Assets/fr.lproj/Vector.strings | 2 +- Riot/Modules/SideMenu/SideMenuItem.swift | 2 +- Riot/Modules/TabBar/TabBarCoordinator.swift | 11 ++++++----- Tchap/Assets/Localizations/fr.lproj/Tchap.strings | 4 ++++ Tchap/Generated/Strings.swift | 2 ++ 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 86d1b9aac3..08e82c15ed 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1829,7 +1829,7 @@ Tap the + to start adding people."; // Mark: - Side menu "side_menu_reveal_action_accessibility_label" = "Left panel"; -"side_menu_action_invite_friends" = "Invite to join Tchap"; +"side_menu_action_invite_friends" = "Invite friends"; "side_menu_action_settings" = "Settings"; "side_menu_action_help" = "Help"; "side_menu_action_feedback" = "Feedback"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 555f527c71..e0ec2d1687 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -1421,7 +1421,7 @@ "side_menu_action_feedback" = "Remarques"; "side_menu_action_help" = "Aide"; "side_menu_action_settings" = "Paramètres"; -"side_menu_action_invite_friends" = "Inviter à rejoindre Tchap"; +"side_menu_action_invite_friends" = "Inviter des amis"; // Mark: - Side menu diff --git a/Riot/Modules/SideMenu/SideMenuItem.swift b/Riot/Modules/SideMenu/SideMenuItem.swift index 9d522dec17..5bbe949db2 100644 --- a/Riot/Modules/SideMenu/SideMenuItem.swift +++ b/Riot/Modules/SideMenu/SideMenuItem.swift @@ -31,7 +31,7 @@ extension SideMenuItem { switch self { case .inviteFriends: - title = VectorL10n.sideMenuActionInviteFriends + title = TchapL10n.sideMenuActionInviteFriends case .settings: title = VectorL10n.sideMenuActionSettings case .help: diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index 10c21a3020..f3c664a42b 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -117,9 +117,6 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { } self.errorPresenter = AlertErrorPresenter(viewControllerPresenter: masterTabBarController) - - guard let session = self.currentMatrixSession else { return } - self.inviteService = InviteService(session: session) } func toPresentable() -> UIViewController { @@ -738,14 +735,14 @@ extension TabBarCoordinator { }) // Cancel action - let cancelAction = UIAlertAction(title: TchapL10n.actionCancel, + let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel) { [weak self] _ in self?.currentAlertController = nil } alertController.addAction(cancelAction) // Invite action - let inviteAction = UIAlertAction(title: TchapL10n.actionInvite, + let inviteAction = UIAlertAction(title: VectorL10n.invite, style: .default) { [weak self] _ in guard let currentAlertController = self?.currentAlertController, let email = currentAlertController.textFields?.first?.text?.lowercased() else { @@ -780,6 +777,10 @@ extension TabBarCoordinator { } private func sendEmailInvite(to email: String) { + guard let session = self.currentMatrixSession else { return } + if self.inviteService == nil { + self.inviteService = InviteService(session: session) + } guard let inviteService = self.inviteService else { return } self.activityIndicatorPresenter.presentActivityIndicator(on: masterTabBarController.view, animated: true) diff --git a/Tchap/Assets/Localizations/fr.lproj/Tchap.strings b/Tchap/Assets/Localizations/fr.lproj/Tchap.strings index e1b8919d11..5895e97ffc 100644 --- a/Tchap/Assets/Localizations/fr.lproj/Tchap.strings +++ b/Tchap/Assets/Localizations/fr.lproj/Tchap.strings @@ -298,3 +298,7 @@ //////////////////////////////////////////////////////////////////////////////// // MARK: Forward "forward_screen_title" = "Transférer à"; + +//////////////////////////////////////////////////////////////////////////////// +// MARK: Side menu +"side_menu_action_invite_friends" = "Inviter à rejoindre Tchap"; diff --git a/Tchap/Generated/Strings.swift b/Tchap/Generated/Strings.swift index f098666a67..bb963964c5 100644 --- a/Tchap/Generated/Strings.swift +++ b/Tchap/Generated/Strings.swift @@ -424,6 +424,8 @@ internal enum TchapL10n { internal static let settingsShowProfileChangesMessagesTitle = TchapL10n.tr("Tchap", "settings_show_profile_changes_messages_title") /// Échec d'envoi. Veuillez renouveler cet envoi depuis l'application internal static let shareExtensionFailedToShareInEmptyDiscussion = TchapL10n.tr("Tchap", "share_extension_failed_to_share_in_empty_discussion") + /// Inviter à rejoindre Tchap + internal static let sideMenuActionInviteFriends = TchapL10n.tr("Tchap", "side_menu_action_invite_friends") /// Votre correspondant a quitté définitivement cette discussion. /// Vous devez en créer une nouvelle pour le recontacter, s'il est toujours joignable sur Tchap. internal static let tchapCannotInviteDeactivatedAccountUser = TchapL10n.tr("Tchap", "tchap_cannot_invite_deactivated_account_user") From cad5ccb837db955995896c3c9d06a8ae8f1b95b9 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 15 Apr 2022 18:01:56 +0200 Subject: [PATCH 3/4] Clean ContactsViewController and ContactsDataSource --- .../Modules/Contacts/ContactsViewController.m | 142 ------------------ .../Contacts/DataSources/ContactsDataSource.m | 10 -- 2 files changed, 152 deletions(-) diff --git a/Tchap/Modules/Contacts/ContactsViewController.m b/Tchap/Modules/Contacts/ContactsViewController.m index de88eefad8..5fe168378c 100644 --- a/Tchap/Modules/Contacts/ContactsViewController.m +++ b/Tchap/Modules/Contacts/ContactsViewController.m @@ -238,128 +238,6 @@ - (void)setupSearchController self.searchController = searchController; } -- (void)promptUserToFillAnEmailToInvite:(void (^)(NSString *email))completion -{ - MXWeakify(self); - - [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; - self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"contacts_invite_by_email_title", @"Tchap", nil) - message:NSLocalizedStringFromTable(@"contacts_invite_by_email_message", @"Tchap", nil) - preferredStyle:UIAlertControllerStyleAlert]; - - [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { - - MXStrongifyAndReturnIfNil(self); - self.currentAlert = nil; - - }]]; - - [self.currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) { - textField.secureTextEntry = NO; - textField.placeholder = nil; - textField.keyboardType = UIKeyboardTypeEmailAddress; - }]; - - [self.currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"action_invite", @"Tchap", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - MXStrongifyAndReturnIfNil(self); - UITextField *textField = [self.currentAlert textFields].firstObject; - // Force the filled email address in lowercase - NSString *email = [textField.text lowercaseString]; - self.currentAlert = nil; - - if ([MXTools isEmailAddress:email]) - { - completion(email); - } - else - { - MXWeakify(self); - [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; - self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"authentication_error_invalid_email", @"Tchap", nil) - message:nil - preferredStyle:UIAlertControllerStyleAlert]; - [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - MXStrongifyAndReturnIfNil(self); - self.currentAlert = nil; - - }]]; - [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailError"]; - [self presentViewController:self.currentAlert animated:YES completion:nil]; - } - }]]; - - [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailDialog"]; - [self presentViewController:self.currentAlert animated:YES completion:nil]; -} - -- (void)sendInviteToTchapByEmail:(NSString *)email -{ - // Sanity check - if ([self.delegate respondsToSelector:@selector(contactsViewController:sendInviteToTchapByEmail:)]) - { - [self.delegate contactsViewController:self sendInviteToTchapByEmail:email]; - } -} - -- (void)selectEmail:(NSString *)email -{ - // Check whether the delegate allows this email to be invited - if ([self.delegate respondsToSelector:@selector(contactsViewController:askPermissionToSelect:completion:)]) - { - [self startActivityIndicator]; - MXWeakify(self); - [self.delegate contactsViewController:self - askPermissionToSelect:email - completion:^(BOOL granted, NSString * _Nullable reason) { - MXStrongifyAndReturnIfNil(self); - [self stopActivityIndicator]; - if (granted) - { - MXKContact *contact = [self.contactsDataSource addSelectedEmail:email]; - if (self.delegate) - { - [self.delegate contactsViewController:self didSelectContact:contact]; - } - } - else - { - MXWeakify(self); - [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; - self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"contacts_picker_unauthorized_email_title", @"Tchap", nil) - message:reason - preferredStyle:UIAlertControllerStyleAlert]; - [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - MXStrongifyAndReturnIfNil(self); - self.currentAlert = nil; - - }]]; - [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailError"]; - [self presentViewController:self.currentAlert animated:YES completion:nil]; - } - }]; - } - else - { - // By default all email is allowed - MXKContact *contact = [self.contactsDataSource addSelectedEmail:email]; - if (self.delegate) - { - [self.delegate contactsViewController:self didSelectContact:contact]; - } - } -} - #pragma mark - Theme - (void)updateTheme @@ -463,16 +341,6 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - // Check first the potential invite button - if ([self.contactsDataSource isInviteButtonIndexPath:indexPath]) - { - [tableView deselectRowAtIndexPath:indexPath animated:YES]; - [self promptUserToFillAnEmailToInvite:^(NSString *email) { - [self sendInviteToTchapByEmail:email]; - }]; - return; - } - // Check whether the user wants to invite people by sharing a link to the room if ([self.contactsDataSource isInviteByLinkButtonIndexPath:indexPath]) { @@ -482,16 +350,6 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath return; } - // Check whether the user wants to add manually some email into the list - if ([self.contactsDataSource isAddEmailButtonIndexPath:indexPath]) - { - [tableView deselectRowAtIndexPath:indexPath animated:YES]; - [self promptUserToFillAnEmailToInvite:^(NSString *email) { - [self selectEmail:email]; - }]; - return; - } - MXKContact *mxkContact = [self.contactsDataSource contactAtIndexPath:indexPath]; if (mxkContact) { diff --git a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m index de54f68a26..2d1b666f62 100644 --- a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m +++ b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m @@ -1216,21 +1216,11 @@ - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *) #pragma mark - --(BOOL)isInviteButtonIndexPath:(NSIndexPath*)indexPath -{ - return (indexPath.section == inviteToTchapButtonSection); -} - -(BOOL)isInviteByLinkButtonIndexPath:(NSIndexPath*)indexPath { return (indexPath.section == inviteByLinkButtonSection); } --(BOOL)isAddEmailButtonIndexPath:(NSIndexPath*)indexPath -{ - return (indexPath.section == addEmailButtonSection); -} - -(MXKContact *)contactAtIndexPath:(NSIndexPath*)indexPath { NSInteger row = indexPath.row; From 6ecf50d557c8e30d592eaa537a5003e9513dae60 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Mon, 25 Apr 2022 09:32:13 +0200 Subject: [PATCH 4/4] Fix latests PR comments --- .../SideMenu/SideMenuCoordinator.swift | 2 +- .../Modules/Contacts/ContactsViewController.m | 123 ++++++++++++++++++ .../Contacts/DataSources/ContactsDataSource.h | 8 -- .../Contacts/DataSources/ContactsDataSource.m | 5 + 4 files changed, 129 insertions(+), 9 deletions(-) diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index ce2667c370..961c63864a 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -284,7 +284,7 @@ extension SideMenuCoordinator: SideMenuViewModelCoordinatorDelegate { self.sideMenuNavigationViewController.dismiss(animated: true) { self.delegate?.sideMenuCoordinator(self, didTapMenuItem: menuItem, fromSourceView: sourceView) } - break + return case .settings: self.showSettings() case .help: diff --git a/Tchap/Modules/Contacts/ContactsViewController.m b/Tchap/Modules/Contacts/ContactsViewController.m index 5fe168378c..38550d941b 100644 --- a/Tchap/Modules/Contacts/ContactsViewController.m +++ b/Tchap/Modules/Contacts/ContactsViewController.m @@ -238,6 +238,119 @@ - (void)setupSearchController self.searchController = searchController; } +- (void)promptUserToFillAnEmailToInvite:(void (^)(NSString *email))completion +{ + MXWeakify(self); + + [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; + self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"contacts_invite_by_email_title", @"Tchap", nil) + message:NSLocalizedStringFromTable(@"contacts_invite_by_email_message", @"Tchap", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + + MXStrongifyAndReturnIfNil(self); + self.currentAlert = nil; + + }]]; + + [self.currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.secureTextEntry = NO; + textField.placeholder = nil; + textField.keyboardType = UIKeyboardTypeEmailAddress; + }]; + + [self.currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"action_invite", @"Tchap", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + MXStrongifyAndReturnIfNil(self); + UITextField *textField = [self.currentAlert textFields].firstObject; + // Force the filled email address in lowercase + NSString *email = [textField.text lowercaseString]; + self.currentAlert = nil; + + if ([MXTools isEmailAddress:email]) + { + completion(email); + } + else + { + MXWeakify(self); + [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; + self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"authentication_error_invalid_email", @"Tchap", nil) + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + MXStrongifyAndReturnIfNil(self); + self.currentAlert = nil; + + }]]; + [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailError"]; + [self presentViewController:self.currentAlert animated:YES completion:nil]; + } + }]]; + + [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailDialog"]; + [self presentViewController:self.currentAlert animated:YES completion:nil]; +} + +- (void)selectEmail:(NSString *)email +{ + // Check whether the delegate allows this email to be invited + if ([self.delegate respondsToSelector:@selector(contactsViewController:askPermissionToSelect:completion:)]) + { + [self startActivityIndicator]; + MXWeakify(self); + [self.delegate contactsViewController:self + askPermissionToSelect:email + completion:^(BOOL granted, NSString * _Nullable reason) { + MXStrongifyAndReturnIfNil(self); + [self stopActivityIndicator]; + if (granted) + { + MXKContact *contact = [self.contactsDataSource addSelectedEmail:email]; + if (self.delegate) + { + [self.delegate contactsViewController:self didSelectContact:contact]; + } + } + else + { + MXWeakify(self); + [self.currentAlert dismissViewControllerAnimated:NO completion:nil]; + self.currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"contacts_picker_unauthorized_email_title", @"Tchap", nil) + message:reason + preferredStyle:UIAlertControllerStyleAlert]; + [self.currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + MXStrongifyAndReturnIfNil(self); + self.currentAlert = nil; + + }]]; + [self.currentAlert mxk_setAccessibilityIdentifier: @"ContactsVCInviteByEmailError"]; + [self presentViewController:self.currentAlert animated:YES completion:nil]; + } + }]; + } + else + { + // By default all email is allowed + MXKContact *contact = [self.contactsDataSource addSelectedEmail:email]; + if (self.delegate) + { + [self.delegate contactsViewController:self didSelectContact:contact]; + } + } +} + #pragma mark - Theme - (void)updateTheme @@ -350,6 +463,16 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath return; } + // Check whether the user wants to add manually some email into the list + if ([self.contactsDataSource isAddEmailButtonIndexPath:indexPath]) + { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + [self promptUserToFillAnEmailToInvite:^(NSString *email) { + [self selectEmail:email]; + }]; + return; + } + MXKContact *mxkContact = [self.contactsDataSource contactAtIndexPath:indexPath]; if (mxkContact) { diff --git a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.h b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.h index 9046dfd9bd..cb46ad449b 100644 --- a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.h +++ b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.h @@ -94,14 +94,6 @@ typedef enum : NSUInteger NSMutableArray *filteredMatrixContacts; } -/** - Check whether the invite button is located to the given index path. - - @param indexPath the index of the cell - @return YES if the indexPath is the invite button one - */ --(BOOL)isInviteButtonIndexPath:(NSIndexPath*)indexPath; - /** Check whether the invite by link button is located to the given index path. diff --git a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m index 2d1b666f62..0e24a3ee59 100644 --- a/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m +++ b/Tchap/Modules/Contacts/DataSources/ContactsDataSource.m @@ -1221,6 +1221,11 @@ -(BOOL)isInviteByLinkButtonIndexPath:(NSIndexPath*)indexPath return (indexPath.section == inviteByLinkButtonSection); } +-(BOOL)isAddEmailButtonIndexPath:(NSIndexPath*)indexPath +{ + return (indexPath.section == addEmailButtonSection); +} + -(MXKContact *)contactAtIndexPath:(NSIndexPath*)indexPath { NSInteger row = indexPath.row;