diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 36a6e454c..121281858 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1900,6 +1900,7 @@ Tap the + to start adding people."; "spaces_invite_people" = "Invite people"; "spaces_add_room" = "Add room"; +"spaces_add_room_missing_permission_message" = "You do not have permissions to add rooms to this space."; "spaces_add_space" = "Add space"; // Mark: Avatar diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index cb5516dca..4e50ae472 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -5415,6 +5415,10 @@ public class VectorL10n: NSObject { public static var spacesAddRoom: String { return VectorL10n.tr("Vector", "spaces_add_room") } + /// You do not have permissions to add rooms to this space. + public static var spacesAddRoomMissingPermissionMessage: String { + return VectorL10n.tr("Vector", "spaces_add_room_missing_permission_message") + } /// Adding rooms coming soon public static var spacesAddRoomsComingSoonTitle: String { return VectorL10n.tr("Vector", "spaces_add_rooms_coming_soon_title") diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index 90fff2f7a..8e49c205a 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -309,6 +309,22 @@ } } +- (void)createNewRoom +{ + if (recentsDataSource.currentSpace) { + [recentsDataSource.currentSpace canAddRoomWithCompletion:^(BOOL canAddRoom) { + if (canAddRoom) { + [super createNewRoom]; + } else { + [[AppDelegate theDelegate] showAlertWithTitle:[VectorL10n roomRecentsCreateEmptyRoom] + message:[VectorL10n spacesAddRoomMissingPermissionMessage]]; + } + }]; + } else { + [super createNewRoom]; + } +} + #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index 30362cac8..c74a6dbd8 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -393,7 +393,15 @@ extension SideMenuCoordinator: SpaceMenuPresenterDelegate { case .exploreMembers: self.showMembers(spaceId: spaceId, session: session) case .addRoom: - self.showAddRoom(spaceId: spaceId, session: session) + session.spaceService.getSpace(withId: spaceId)?.canAddRoom { canAddRoom in + if canAddRoom { + self.showAddRoom(spaceId: spaceId, session: session) + } else { + let alert = UIAlertController(title: VectorL10n.spacesAddRoom, message: VectorL10n.spacesAddRoomMissingPermissionMessage, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: VectorL10n.ok, style: .default, handler: nil)) + self.toPresentable().present(alert, animated: true, completion: nil) + } + } case .addSpace: AppDelegate.theDelegate().showAlert(withTitle: VectorL10n.spacesAddSpace, message: VectorL10n.spacesComingSoonDetail(AppInfo.current.displayName)) case .settings: diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift index 39149bde3..39c4b4d74 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift @@ -27,6 +27,7 @@ final class SpaceExploreRoomCoordinator: SpaceExploreRoomCoordinatorType { private var spaceExploreRoomViewModel: SpaceExploreRoomViewModelType private let spaceExploreRoomViewController: SpaceExploreRoomViewController + private let parameters: SpaceExploreRoomCoordinatorParameters // MARK: Public @@ -42,6 +43,7 @@ final class SpaceExploreRoomCoordinator: SpaceExploreRoomCoordinatorType { let spaceExploreRoomViewController = SpaceExploreRoomViewController.instantiate(with: spaceExploreRoomViewModel) self.spaceExploreRoomViewModel = spaceExploreRoomViewModel self.spaceExploreRoomViewController = spaceExploreRoomViewController + self.parameters = parameters } // MARK: - Public methods @@ -70,6 +72,23 @@ extension SpaceExploreRoomCoordinator: SpaceExploreRoomViewModelCoordinatorDeleg } func spaceExploreRoomViewModelDidAddRoom(_ viewModel: SpaceExploreRoomViewModelType) { - self.delegate?.spaceExploreRoomCoordinatorDidAddRoom(self) + guard let space = parameters.session.spaceService.getSpace(withId: parameters.spaceId) else { + showAddRoomMissingPermissionAlert() + return + } + + space.canAddRoom { canAddRoom in + if canAddRoom { + self.delegate?.spaceExploreRoomCoordinatorDidAddRoom(self) + } else { + self.showAddRoomMissingPermissionAlert() + } + } + } + + private func showAddRoomMissingPermissionAlert() { + let alert = UIAlertController(title: VectorL10n.spacesAddRoom, message: VectorL10n.spacesAddRoomMissingPermissionMessage, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: VectorL10n.ok, style: .default, handler: nil)) + self.toPresentable().present(alert, animated: true, completion: nil) } } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorParameters.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorParameters.swift index cc3c97f76..3e291f5b3 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorParameters.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorParameters.swift @@ -20,4 +20,5 @@ struct SpaceExploreRoomCoordinatorParameters { let session: MXSession let spaceId: String let spaceName: String? + let showCancelMenuItem: Bool } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift index 246469649..3aae0f838 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift @@ -24,6 +24,7 @@ enum SpaceExploreRoomViewAction { case loadData case complete(_ selectedItem: SpaceExploreRoomListItemViewData, _ sourceView: UIView?) case searchChanged(_ text: String?) + case join case cancel case addRoom } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift index d1e03eda9..21a246cf9 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift @@ -125,12 +125,14 @@ final class SpaceExploreRoomViewController: UIViewController { } private func setupViews() { - let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in - self?.cancelButtonAction() + if viewModel.showCancelMenuItem { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.leftBarButtonItem = cancelBarButtonItem } - - self.navigationItem.rightBarButtonItem = cancelBarButtonItem - + self.vc_removeBackTitle() self.titleView = MainTitleView() @@ -145,6 +147,18 @@ final class SpaceExploreRoomViewController: UIViewController { self.setupTableViewHeader() } + private func setupJoinButton(canJoin: Bool) { + if canJoin { + let joinButtonItem = MXKBarButtonItem(title: VectorL10n.join, style: .done) { [weak self] in + self?.viewModel.process(viewAction: .join) + } + + self.navigationItem.rightBarButtonItem = joinButtonItem + } else { + self.navigationItem.rightBarButtonItem = nil + } + } + private func setupTableViewHeader() { addRoomHeaderView.delegate = self tableView.tableHeaderView = addRoomHeaderView @@ -176,6 +190,8 @@ final class SpaceExploreRoomViewController: UIViewController { self.renderEmptyFilterResult() case .error(let error): self.render(error: error) + case .canJoin(let canJoin): + self.setupJoinButton(canJoin: canJoin) } } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift index 9ca53298e..c7ce1597a 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift @@ -17,6 +17,7 @@ */ import Foundation +import MatrixSDK final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { @@ -32,6 +33,11 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { private var nextBatch: String? private var rootSpaceChildInfo: MXSpaceChildInfo? + private var canJoin: Bool = false { + didSet { + self.update(viewState: .canJoin(self.canJoin)) + } + } private var itemDataList: [SpaceExploreRoomListItemViewData] = [] { didSet { self.updateFilteredItemList() @@ -56,21 +62,26 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { } } + private var spaceGraphObserver: Any? + // MARK: Public weak var viewDelegate: SpaceExploreRoomViewModelViewDelegate? weak var coordinatorDelegate: SpaceExploreRoomViewModelCoordinatorDelegate? - + private(set) var showCancelMenuItem: Bool + // MARK: - Setup init(parameters: SpaceExploreRoomCoordinatorParameters) { self.session = parameters.session self.spaceId = parameters.spaceId self.spaceName = parameters.spaceName + self.showCancelMenuItem = parameters.showCancelMenuItem } deinit { self.cancelOperations() + } // MARK: - Public @@ -91,6 +102,8 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { self.searchKeyword = newText case .addRoom: self.coordinatorDelegate?.spaceExploreRoomViewModelDidAddRoom(self) + case .join: + self.joinSpace() } } @@ -109,6 +122,8 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { self.update(viewState: .loading) } + self.canJoin = self.session.room(withRoomId: spaceId) == nil + self.currentOperation = self.session.spaceService.getSpaceChildrenForSpace(withId: self.spaceId, suggestedOnly: false, limit: nil, maxDepth: 1, paginationToken: self.nextBatch, completion: { [weak self] response in guard let self = self else { return @@ -158,6 +173,9 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { private func cancelOperations() { self.currentOperation?.cancel() + if let observer = self.spaceGraphObserver { + NotificationCenter.default.removeObserver(observer) + } } private func updateFilteredItemList() { @@ -170,4 +188,26 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { return (itemData.childInfo.name?.lowercased().contains(searchKeyword) ?? false) || (itemData.childInfo.topic?.lowercased().contains(searchKeyword) ?? false) }) } + + private func joinSpace() { + self.update(viewState: .loading) + + self.currentOperation = session.joinRoom(spaceId) { [weak self] response in + switch response { + case .success: + self?.spaceGraphObserver = NotificationCenter.default.addObserver(forName: MXSpaceService.didBuildSpaceGraph, object: nil, queue: OperationQueue.main, using: { [weak self] notification in + guard let self = self else { return } + + self.currentOperation = nil + if let observer = self.spaceGraphObserver { + NotificationCenter.default.removeObserver(observer) + } + self.canJoin = false + self.update(viewState: .loaded(self.filteredItemDataList, self.nextBatch != nil && (self.searchKeyword ?? "").isEmpty)) + }) + case .failure(let error): + self?.update(viewState: .error(error)) + } + } + } } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift index be08afe74..56d236200 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift @@ -33,6 +33,7 @@ protocol SpaceExploreRoomViewModelType { var viewDelegate: SpaceExploreRoomViewModelViewDelegate? { get set } var coordinatorDelegate: SpaceExploreRoomViewModelCoordinatorDelegate? { get set } - + var showCancelMenuItem: Bool { get } + func process(viewAction: SpaceExploreRoomViewAction) } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewState.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewState.swift index 52203c190..51f784596 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewState.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewState.swift @@ -23,6 +23,7 @@ enum SpaceExploreRoomViewState { case loading case spaceNameFound(_ spaceName: String) case loaded(_ children: [SpaceExploreRoomListItemViewData], _ hasMore: Bool) + case canJoin(Bool) case emptySpace case emptyFilterResult case error(Error) diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift index 93eb57bb6..4adfaf8aa 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift @@ -124,7 +124,7 @@ final class ExploreRoomCoordinator: NSObject, ExploreRoomCoordinatorType { } private func createShowSpaceExploreRoomCoordinator(session: MXSession, spaceId: String, spaceName: String?) -> SpaceExploreRoomCoordinator { - let coordinator = SpaceExploreRoomCoordinator(parameters: SpaceExploreRoomCoordinatorParameters(session: session, spaceId: spaceId, spaceName: spaceName)) + let coordinator = SpaceExploreRoomCoordinator(parameters: SpaceExploreRoomCoordinatorParameters(session: session, spaceId: spaceId, spaceName: spaceName, showCancelMenuItem: self.navigationRouter.modules.isEmpty)) coordinator.delegate = self return coordinator } diff --git a/changelog.d/5230.feature b/changelog.d/5230.feature new file mode 100644 index 000000000..825e377f7 --- /dev/null +++ b/changelog.d/5230.feature @@ -0,0 +1 @@ +Adding Rooms to Spaces \ No newline at end of file