// // Copyright 2021 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation @objc protocol RoomParticipantsInviteCoordinatorBridgePresenterDelegate { func roomParticipantsInviteCoordinatorBridgePresenterDidStartLoading(_ coordinatorBridgePresenter: RoomParticipantsInviteCoordinatorBridgePresenter) func roomParticipantsInviteCoordinatorBridgePresenterDidEndLoading(_ coordinatorBridgePresenter: RoomParticipantsInviteCoordinatorBridgePresenter) func roomParticipantsInviteCoordinatorBridgePresenterDidComplete(_ coordinatorBridgePresenter: RoomParticipantsInviteCoordinatorBridgePresenter) } /// RoomParticipantsInviteCoordinatorBridgePresenter enables to start ContactsPickerCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. @objcMembers final class RoomParticipantsInviteCoordinatorBridgePresenter: NSObject { // MARK: - Properties // MARK: Private private let session: MXSession? private let room: MXRoom? private let parentSpaceId: String? private let currentSearchText: String? private var actualParticipants: [Contact]? private var invitedParticipants: [Contact]? private var userParticipant: Contact? private var roomOptions: [RoomOptionListItemViewData] = [] private weak var contactsPickerViewController: ContactsTableViewController? private weak var currentAlert: UIAlertController? private var contactPickerCoordinator: ContactsPickerCoordinator? private var optionListCoordinator: OptionListCoordinator? private var navigationRouter: NavigationRouterType? // MARK: Public weak var delegate: RoomParticipantsInviteCoordinatorBridgePresenterDelegate? // MARK: - Setup init(session: MXSession?, room: MXRoom?, parentSpaceId: String?) { self.session = session self.room = room self.parentSpaceId = parentSpaceId self.currentSearchText = nil self.actualParticipants = nil self.invitedParticipants = nil self.userParticipant = nil super.init() } init(session: MXSession?, room: MXRoom?, parentSpaceId: String?, currentSearchText: String? = nil, actualParticipants: [Contact]? = nil, invitedParticipants: [Contact]? = nil, userParticipant: Contact? = nil) { self.session = session self.room = room self.parentSpaceId = parentSpaceId self.currentSearchText = currentSearchText self.actualParticipants = actualParticipants self.invitedParticipants = invitedParticipants self.userParticipant = userParticipant super.init() } func present(from viewController: UIViewController, animated: Bool) { guard let room = self.room else { MXLog.error("[RoomParticipantsInviteCoordinatorBridgePresenter] present: nil room found") return } if let navigationController = viewController.navigationController { self.navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController) } else { self.navigationRouter = nil } if let spaceId = self.parentSpaceId, let spaceRoom = self.session?.spaceService.getSpace(withId: spaceId)?.room { self.presentRoomSelector(between: room, and: spaceRoom) return } self.pushContactsPicker(for: room) } // MARK: - Private private class RoomOptionListItemViewData: OptionListItemViewData { let room: MXRoom init(title: String? = nil, detail: String? = nil, image: UIImage? = nil, room: MXRoom, accessoryImage: UIImage? = Asset.Images.chevron.image, enabled: Bool = true) { self.room = room super.init(title: title, detail: detail, image: image, accessoryImage: accessoryImage, enabled: enabled) } } private func presentRoomSelector(between room: MXRoom, and spaceRoom: MXRoom) { let roomName = room.displayName ?? "" let spaceName = spaceRoom.displayName ?? "" self.roomOptions = [ RoomOptionListItemViewData(title: VectorL10n.roomInviteToSpaceOptionTitle(spaceName), detail: VectorL10n.roomInviteToSpaceOptionDetail(spaceName, roomName), image: Asset.Images.addParticipants.image, room: spaceRoom, accessoryImage: Asset.Images.chevron.image), RoomOptionListItemViewData(title: VectorL10n.roomInviteToRoomOptionTitle, detail: VectorL10n.roomInviteToRoomOptionDetail(spaceName), image: Asset.Images.addParticipants.image, room: room, accessoryImage: Asset.Images.chevron.image) ] let coordinator = OptionListCoordinator(parameters: OptionListCoordinatorParameters(title: VectorL10n.roomIntroCellAddParticipantsAction, options: self.roomOptions, navigationRouter: self.navigationRouter)) coordinator.delegate = self coordinator.start() self.optionListCoordinator = coordinator } private func pushContactsPicker(for room: MXRoom) { guard let session = self.session else { MXLog.error("[RoomParticipantsInviteCoordinatorBridgePresenter] pushContactsPicker: nil session found") return } canInvite(to: room) { [weak self] canInvite in guard let self = self else { return } guard canInvite else { let message = room.summary?.roomType == .space ? VectorL10n.spaceInviteNotEnoughPermission : VectorL10n.roomInviteNotEnoughPermission let alert = UIAlertController(title: VectorL10n.spacesInvitePeople, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: VectorL10n.ok, style: .default, handler: nil)) self.navigationRouter?.present(alert, animated: true) return } let coordinator = ContactsPickerCoordinator(session: session, room: room, initialSearchText: self.currentSearchText, actualParticipants: self.actualParticipants, invitedParticipants: self.invitedParticipants, userParticipant: self.userParticipant, navigationRouter: self.navigationRouter) coordinator.delegate = self coordinator.start() self.contactPickerCoordinator = coordinator } } private func canInvite(to room: MXRoom, completion: @escaping (Bool) -> Void) { guard let userId = self.session?.myUserId else { MXLog.error("[RoomParticipantsInviteCoordinatorBridgePresenter] canInvite: userId not found") completion(false) return } room.state { roomState in guard let powerLevels = roomState?.powerLevels else { MXLog.error("[RoomParticipantsInviteCoordinatorBridgePresenter] canInvite: room powerLevels not found") completion(false) return } let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: userId) completion(userPowerLevel >= powerLevels.invite) } } } extension RoomParticipantsInviteCoordinatorBridgePresenter: ContactsPickerCoordinatorDelegate { func contactsPickerCoordinatorDidStartLoading(_ coordinator: ContactsPickerCoordinatorProtocol) { delegate?.roomParticipantsInviteCoordinatorBridgePresenterDidStartLoading(self) } func contactsPickerCoordinatorDidEndLoading(_ coordinator: ContactsPickerCoordinatorProtocol) { delegate?.roomParticipantsInviteCoordinatorBridgePresenterDidEndLoading(self) } func contactsPickerCoordinatorDidClose(_ coordinator: ContactsPickerCoordinatorProtocol) { delegate?.roomParticipantsInviteCoordinatorBridgePresenterDidComplete(self) } } extension RoomParticipantsInviteCoordinatorBridgePresenter: OptionListCoordinatorDelegate { func optionListCoordinator(_ coordinator: OptionListCoordinatorProtocol, didSelectOptionAt index: Int) { optionListCoordinator = nil self.pushContactsPicker(for: roomOptions[index].room) } func optionListCoordinator(_ coordinator: OptionListCoordinatorProtocol, didCompleteWithUserDisplayName userDisplayName: String?) { optionListCoordinator = nil } func optionListCoordinatorDidCancel(_ coordinator: OptionListCoordinatorProtocol) { optionListCoordinator = nil } }