diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 1b053ee46..866a2b385 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -73,6 +73,7 @@ "existing" = "Existing"; "add" = "Add"; "ok" = "OK"; +"suggest" = "Suggest"; // Call Bar "callbar_only_single_active" = "Tap to return to the call (%@)"; @@ -1803,6 +1804,8 @@ Tap the + to start adding people."; "leave_space_and_all_rooms_action" = "Leave all rooms and spaces"; "spaces_explore_rooms" = "Explore rooms"; "spaces_suggested_room" = "Suggested"; +"spaces_explore_rooms_room_number" = "%@ rooms"; +"spaces_explore_rooms_one_room" = "1 room"; "space_tag" = "space"; "spaces_empty_space_title" = "This space has no rooms (yet)"; "spaces_empty_space_detail" = "Some rooms may be hidden because they’re private and you need an invite."; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 07f71dfb9..e6cde88c2 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -279,6 +279,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: SpaceMenuViewController.self) } + internal enum SpaceRoomPreviewViewController: StoryboardType { + internal static let storyboardName = "SpaceRoomPreviewViewController" + + internal static let initialScene = InitialSceneType(storyboard: SpaceRoomPreviewViewController.self) + } internal enum TemplateScreenViewController: StoryboardType { internal static let storyboardName = "TemplateScreenViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 89c1826fe..aeb00b04a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -5563,6 +5563,14 @@ public class VectorL10n: NSObject { public static var spacesExploreRooms: String { return VectorL10n.tr("Vector", "spaces_explore_rooms") } + /// 1 room + public static var spacesExploreRoomsOneRoom: String { + return VectorL10n.tr("Vector", "spaces_explore_rooms_one_room") + } + /// %@ rooms + public static func spacesExploreRoomsRoomNumber(_ p1: String) -> String { + return VectorL10n.tr("Vector", "spaces_explore_rooms_room_number", p1) + } /// Home public static var spacesHomeSpaceTitle: String { return VectorL10n.tr("Vector", "spaces_home_space_title") @@ -5615,6 +5623,10 @@ public class VectorL10n: NSObject { public static var storeShortDescription: String { return VectorL10n.tr("Vector", "store_short_description") } + /// Suggest + public static var suggest: String { + return VectorL10n.tr("Vector", "suggest") + } /// Switch public static var `switch`: String { return VectorL10n.tr("Vector", "switch") diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift index 39149bde3..247b28848 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinator.swift @@ -61,6 +61,14 @@ final class SpaceExploreRoomCoordinator: SpaceExploreRoomCoordinatorType { // MARK: - SpaceExploreRoomViewModelCoordinatorDelegate extension SpaceExploreRoomCoordinator: SpaceExploreRoomViewModelCoordinatorDelegate { + func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, openSettingsOf item: SpaceExploreRoomListItemViewData) { + self.delegate?.spaceExploreRoomCoordinatorDidAddRoom(self, openSettingsOf: item) + } + + func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, inviteTo item: SpaceExploreRoomListItemViewData) { + self.delegate?.spaceExploreRoomCoordinatorDidAddRoom(self, inviteTo: item) + } + func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, didSelect item: SpaceExploreRoomListItemViewData, from sourceView: UIView?) { self.delegate?.spaceExploreRoomCoordinator(self, didSelect: item, from: sourceView) } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorType.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorType.swift index cdeaf71ec..339502323 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomCoordinatorType.swift @@ -22,6 +22,8 @@ protocol SpaceExploreRoomCoordinatorDelegate: AnyObject { func spaceExploreRoomCoordinator(_ coordinator: SpaceExploreRoomCoordinatorType, didSelect item: SpaceExploreRoomListItemViewData, from sourceView: UIView?) func spaceExploreRoomCoordinatorDidCancel(_ coordinator: SpaceExploreRoomCoordinatorType) func spaceExploreRoomCoordinatorDidAddRoom(_ coordinator: SpaceExploreRoomCoordinatorType) + func spaceExploreRoomCoordinatorDidAddRoom(_ viewModel: SpaceExploreRoomCoordinatorType, openSettingsOf item: SpaceExploreRoomListItemViewData) + func spaceExploreRoomCoordinatorDidAddRoom(_ viewModel: SpaceExploreRoomCoordinatorType, inviteTo item: SpaceExploreRoomListItemViewData) } /// `SpaceExploreRoomCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift index 246469649..2bb81c305 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewAction.swift @@ -26,4 +26,9 @@ enum SpaceExploreRoomViewAction { case searchChanged(_ text: String?) case cancel case addRoom + case inviteTo(_ item: SpaceExploreRoomListItemViewData) + case revertSuggestion(_ item: SpaceExploreRoomListItemViewData) + case settings(_ item: SpaceExploreRoomListItemViewData) + case removeChild(_ item: SpaceExploreRoomListItemViewData) + case join(_ item: SpaceExploreRoomListItemViewData) } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift index d1e03eda9..2a83cc73b 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewController.swift @@ -256,6 +256,16 @@ extension SpaceExploreRoomViewController: UITableViewDelegate { self.viewModel.process(viewAction: .loadData) } } + + @available(iOS 13.0, *) + func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + let viewData = self.itemDataList[indexPath.row] + return UIContextMenuConfiguration(identifier: nil) { + return SpaceRoomPreviewViewController.instantiate(with: viewData.childInfo, avatarViewData: viewData.avatarViewData) + } actionProvider: { suggestedActions in + return self.viewModel.contextMenu(for: self.itemDataList[indexPath.row]) + } + } } // MARK: - SpaceExploreRoomViewModelViewDelegate diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift index 9ca53298e..b7b3ebc4e 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModel.swift @@ -32,6 +32,10 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { private var nextBatch: String? private var rootSpaceChildInfo: MXSpaceChildInfo? + private var spaceRoom: MXRoom? + private var powerLevels: MXRoomPowerLevels? + private var oneSelfPowerLevel: Int? + private var itemDataList: [SpaceExploreRoomListItemViewData] = [] { didSet { self.updateFilteredItemList() @@ -91,9 +95,39 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { self.searchKeyword = newText case .addRoom: self.coordinatorDelegate?.spaceExploreRoomViewModelDidAddRoom(self) + case .inviteTo(let item): + self.coordinatorDelegate?.spaceExploreRoomViewModel(self, inviteTo: item) + case .revertSuggestion(let item): + setChild(withRoomId: item.childInfo.childRoomId, suggested: !item.childInfo.suggested) + case .settings(let item): + self.coordinatorDelegate?.spaceExploreRoomViewModel(self, openSettingsOf: item) + case .removeChild(let item): + removeChild(withRoomId: item.childInfo.childRoomId) + case .join(let item): + joinRoom(withRoomId: item.childInfo.childRoomId) } } + @available(iOS 13.0, *) + func contextMenu(for itemData: SpaceExploreRoomListItemViewData) -> UIMenu { + let canSendSpaceStateEvents: Bool + if let powerLevels = self.powerLevels, let oneSelfPowerLevel = self.oneSelfPowerLevel { + let minimumPowerLevel = powerLevels.minimumPowerLevel(forNotifications: kMXEventTypeStringRoomJoinRules, defaultPower: powerLevels.stateDefault) + canSendSpaceStateEvents = oneSelfPowerLevel >= minimumPowerLevel + } else { + canSendSpaceStateEvents = false + } + + let roomSummary = session.roomSummary(withRoomId: itemData.childInfo.childRoomId) + let isJoined = roomSummary?.isJoined ?? false + + if itemData.childInfo.roomType == .space { + return contextMenu(forSpace: itemData, isJoined: isJoined, canSendSpaceStateEvents: canSendSpaceStateEvents) + } else { + return contextMenu(forRoom: itemData, isJoined: isJoined, canSendSpaceStateEvents: canSendSpaceStateEvents) + } + } + // MARK: - Private private func loadData() { @@ -109,6 +143,15 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { self.update(viewState: .loading) } + self.spaceRoom = self.session.spaceService.getSpace(withId: self.spaceId)?.room + if let spaceRoom = self.spaceRoom { + spaceRoom.state { roomState in + self.powerLevels = roomState?.powerLevels + self.oneSelfPowerLevel = self.powerLevels?.powerLevelOfUser(withUserID: self.session.myUserId) + + } + } + 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 @@ -170,4 +213,154 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType { return (itemData.childInfo.name?.lowercased().contains(searchKeyword) ?? false) || (itemData.childInfo.topic?.lowercased().contains(searchKeyword) ?? false) }) } + + private func setChild(withRoomId roomId: String, suggested: Bool) { + guard let space = session.spaceService.getSpace(withId: spaceId) else { + return + } + + self.update(viewState: .loading) + space.setChild(withRoomId: roomId, suggested: suggested) { [weak self] response in + guard let self = self else { return } + + switch response { + case .success: + self.itemDataList = self.itemDataList.compactMap({ item in + if item.childInfo.childRoomId != roomId { + return item + } + + let childInfo = MXSpaceChildInfo( + childRoomId: item.childInfo.childRoomId, + isKnown: item.childInfo.isKnown, + roomTypeString: item.childInfo.roomTypeString, + roomType: item.childInfo.roomType, + name: item.childInfo.name, + topic: item.childInfo.topic, + canonicalAlias: item.childInfo.canonicalAlias, + avatarUrl: item.childInfo.avatarUrl, + activeMemberCount: item.childInfo.activeMemberCount, + autoJoin: item.childInfo.autoJoin, + suggested: suggested, + childrenIds: item.childInfo.childrenIds) + return SpaceExploreRoomListItemViewData(childInfo: childInfo, avatarViewData: item.avatarViewData) + }) + self.update(viewState: .loaded(self.filteredItemDataList, self.nextBatch != nil && (self.searchKeyword ?? "").isEmpty)) + case .failure(let error): + self.update(viewState: .error(error)) + } + } + } + + private func removeChild(withRoomId roomId: String) { + guard let space = session.spaceService.getSpace(withId: spaceId) else { + return + } + + self.update(viewState: .loading) + space.removeChild(roomId: roomId) { [weak self] response in + guard let self = self else { return } + + switch response { + case .success: + self.itemDataList = self.itemDataList.filter { $0.childInfo.childRoomId != roomId } + self.update(viewState: .loaded(self.filteredItemDataList, self.nextBatch != nil && (self.searchKeyword ?? "").isEmpty)) + case .failure(let error): + self.update(viewState: .error(error)) + } + } + } + + private func joinRoom(withRoomId roomId: String) { + self.update(viewState: .loading) + self.session.joinRoom(roomId) { [weak self] response in + guard let self = self else { return } + switch response { + case .success: + self.update(viewState: .loaded(self.filteredItemDataList, self.nextBatch != nil && (self.searchKeyword ?? "").isEmpty)) + case .failure(let error): + self.update(viewState: .error(error)) + } + } + } + + + // MARK: - ContextMenu + + @available(iOS 13.0, *) + private func contextMenu(forRoom itemData: SpaceExploreRoomListItemViewData, isJoined: Bool, canSendSpaceStateEvents: Bool) -> UIMenu { + return UIMenu(children: [ + inviteAction(for: itemData, isJoined: isJoined), + suggestAction(for: itemData, canSendSpaceStateEvents: canSendSpaceStateEvents), + isJoined ? settingsAction(for: itemData, isJoined: isJoined) :joinAction(for: itemData, isJoined: isJoined), + removeAction(for: itemData, canSendSpaceStateEvents: canSendSpaceStateEvents) + ]) + } + + @available(iOS 13.0, *) + private func contextMenu(forSpace itemData: SpaceExploreRoomListItemViewData, isJoined: Bool, canSendSpaceStateEvents: Bool) -> UIMenu { + return UIMenu(children: [ + inviteAction(for: itemData, isJoined: isJoined), + suggestAction(for: itemData, canSendSpaceStateEvents: canSendSpaceStateEvents), + isJoined ? settingsAction(for: itemData, isJoined: isJoined) :joinAction(for: itemData, isJoined: isJoined), + removeAction(for: itemData, canSendSpaceStateEvents: canSendSpaceStateEvents) + ]) + } + + @available(iOS 13.0, *) + private func inviteAction(for itemData: SpaceExploreRoomListItemViewData, isJoined: Bool) -> UIAction { + let action = UIAction(title: VectorL10n.invite, image: Asset.Images.spaceInviteUser.image) { action in + self.process(viewAction: .inviteTo(itemData)) + } + if !isJoined { + action.attributes = .disabled + } + return action + } + + @available(iOS 13.0, *) + private func suggestAction(for itemData: SpaceExploreRoomListItemViewData, canSendSpaceStateEvents: Bool) -> UIAction { + let action = UIAction(title: itemData.childInfo.suggested ? VectorL10n.spacesSuggestedRoom : VectorL10n.suggest) { action in + self.process(viewAction: .revertSuggestion(itemData)) + } + action.state = itemData.childInfo.suggested ? .on : .off + if !canSendSpaceStateEvents { + action.attributes = .disabled + } + return action + } + + @available(iOS 13.0, *) + private func settingsAction(for itemData: SpaceExploreRoomListItemViewData, isJoined: Bool) -> UIAction { + let action = UIAction(title: VectorL10n.roomDetailsSettings, image: Asset.Images.settingsIcon.image) { action in + self.process(viewAction: .settings(itemData)) + } + if !isJoined { + action.attributes = .disabled + } + return action + } + + @available(iOS 13.0, *) + private func joinAction(for itemData: SpaceExploreRoomListItemViewData, isJoined: Bool) -> UIAction { + let action = UIAction(title: VectorL10n.join) { action in + self.process(viewAction: .join(itemData)) + } + if !isJoined { + action.attributes = .disabled + } + return action + } + + @available(iOS 13.0, *) + private func removeAction(for itemData: SpaceExploreRoomListItemViewData, canSendSpaceStateEvents: Bool) -> UIAction { + let action = UIAction(title: VectorL10n.remove, image: Asset.Images.roomContextMenuDelete.image) { action in + self.process(viewAction: .removeChild(itemData)) + } + action.attributes = .destructive + if !canSendSpaceStateEvents { + action.attributes = .disabled + } + return action + } } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift index be08afe74..10e0da922 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceExploreRoomViewModelType.swift @@ -26,6 +26,8 @@ protocol SpaceExploreRoomViewModelCoordinatorDelegate: AnyObject { func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, didSelect item: SpaceExploreRoomListItemViewData, from sourceView: UIView?) func spaceExploreRoomViewModelDidCancel(_ viewModel: SpaceExploreRoomViewModelType) func spaceExploreRoomViewModelDidAddRoom(_ viewModel: SpaceExploreRoomViewModelType) + func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, openSettingsOf item: SpaceExploreRoomListItemViewData) + func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, inviteTo item: SpaceExploreRoomListItemViewData) } /// Protocol describing the view model used by `SpaceExploreRoomViewController` @@ -35,4 +37,6 @@ protocol SpaceExploreRoomViewModelType { var coordinatorDelegate: SpaceExploreRoomViewModelCoordinatorDelegate? { get set } func process(viewAction: SpaceExploreRoomViewAction) + @available(iOS 13.0, *) + func contextMenu(for itemData: SpaceExploreRoomListItemViewData) -> UIMenu } diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.storyboard b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.storyboard new file mode 100644 index 000000000..d1e93d6f6 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.storyboard @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.swift new file mode 100644 index 000000000..57b7af624 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoom/SpaceRoomPreviewViewController.swift @@ -0,0 +1,156 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceRoomList/SpaceChildRoomDetail ShowSpaceChildRoomDetail +/* + 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 UIKit + +final class SpaceRoomPreviewViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let popoverWidth: CGFloat = 300 + } + + // MARK: Outlets + + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var avatarView: RoomAvatarView! + @IBOutlet private weak var spaceAvatarView: SpaceAvatarView! + @IBOutlet private weak var userIconView: UIImageView! + @IBOutlet private weak var membersLabel: UILabel! + @IBOutlet private weak var roomsIconView: UIImageView! + @IBOutlet private weak var roomsLabel: UILabel! + @IBOutlet private weak var topicLabel: UILabel! + @IBOutlet private weak var topicLabelBottomMargin: NSLayoutConstraint! + @IBOutlet private weak var spaceTagView: UIView! + @IBOutlet private weak var spaceTagLabel: UILabel! + + // MARK: Private + + private var theme: Theme! + private var roomInfo: MXSpaceChildInfo! + private var avatarViewData: AvatarViewDataProtocol! + + // MARK: - Setup + + class func instantiate(with roomInfo: MXSpaceChildInfo, avatarViewData: AvatarViewDataProtocol!) -> SpaceRoomPreviewViewController { + let viewController = StoryboardScene.SpaceRoomPreviewViewController.initialScene.instantiate() + viewController.roomInfo = roomInfo + viewController.avatarViewData = avatarViewData + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + setupView() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override var preferredContentSize: CGSize { + get { + return CGSize(width: Constants.popoverWidth, height: self.intrisicHeight(with: Constants.popoverWidth)) + } + set { + super.preferredContentSize = newValue + } + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.titleLabel.textColor = theme.textPrimaryColor + self.titleLabel.font = theme.fonts.title3SB + + self.membersLabel.font = theme.fonts.caption1 + self.membersLabel.textColor = theme.colors.tertiaryContent + + self.topicLabel.font = theme.fonts.caption1 + self.topicLabel.textColor = theme.colors.tertiaryContent + + self.userIconView.tintColor = theme.colors.tertiaryContent + + self.roomsIconView.tintColor = theme.colors.tertiaryContent + self.roomsLabel.font = theme.fonts.caption1 + self.roomsLabel.textColor = theme.colors.tertiaryContent + + self.spaceTagView.backgroundColor = theme.colors.quinaryContent + self.spaceTagLabel.font = theme.fonts.caption1 + self.spaceTagLabel.textColor = theme.colors.tertiaryContent + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupView() { + self.titleLabel.text = roomInfo.displayName + + self.spaceTagView.layer.masksToBounds = true + self.spaceTagView.layer.cornerRadius = 2 + self.spaceTagView.isHidden = roomInfo.roomType != .space + self.spaceTagLabel.text = VectorL10n.spaceTag + + self.avatarView.isHidden = roomInfo.roomType == .space + self.spaceAvatarView.isHidden = roomInfo.roomType != .space + + if !self.avatarView.isHidden { + self.avatarView.fill(with: avatarViewData) + } + if !self.spaceAvatarView.isHidden { + self.spaceAvatarView.fill(with: avatarViewData) + } + self.membersLabel.text = roomInfo.activeMemberCount == 1 ? VectorL10n.roomTitleOneMember : VectorL10n.roomTitleMembers("\(roomInfo.activeMemberCount)") + if roomInfo.childrenIds.count == 1 { + self.roomsLabel.text = VectorL10n.spacesExploreRoomsOneRoom + } else { + self.roomsLabel.text = VectorL10n.spacesExploreRoomsRoomNumber("\(roomInfo.childrenIds.count)") + } + self.topicLabel.text = roomInfo.topic + topicLabelBottomMargin.constant = self.topicLabel.text.isEmptyOrNil ? 0 : 16 + + self.roomsIconView.isHidden = roomInfo.roomType != .space + self.roomsLabel.isHidden = roomInfo.roomType != .space + } + + private func intrisicHeight(with width: CGFloat) -> CGFloat { + if self.topicLabel.text.isEmptyOrNil { + return self.topicLabel.frame.minY + } + + let topicHeight = self.topicLabel.sizeThatFits(CGSize(width: width - self.topicLabel.frame.minX * 2, height: 0)).height + return self.topicLabel.frame.minY + topicHeight + 16 + } +} diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift index c3058b0c4..54b084687 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift @@ -173,6 +173,18 @@ final class ExploreRoomCoordinator: NSObject, ExploreRoomCoordinatorType { self.navigationRouter.popToRootModule(animated: animated) } } + + private func pushInviteScreen(forRoomWithId roomId: String) { + guard let room = session.room(withRoomId: roomId) else { + MXLog.error("[ExploreRoomCoordinator] pushInviteScreen: room not found.") + return + } + + let coordinator = ContactsPickerCoordinator(session: session, room: room, currentSearchText: nil, actualParticipants: nil, invitedParticipants: nil, userParticipant: nil, navigationRouter: navigationRouter) + coordinator.delegate = self + coordinator.start() + childCoordinators.append(coordinator) + } } // MARK: - ShowSpaceExploreRoomCoordinatorDelegate @@ -192,6 +204,14 @@ extension ExploreRoomCoordinator: SpaceExploreRoomCoordinatorDelegate { func spaceExploreRoomCoordinatorDidAddRoom(_ coordinator: SpaceExploreRoomCoordinatorType) { self.presentRoomCreation() } + + func spaceExploreRoomCoordinatorDidAddRoom(_ viewModel: SpaceExploreRoomCoordinatorType, openSettingsOf item: SpaceExploreRoomListItemViewData) { + self.navigateTo(roomWith: item.childInfo.childRoomId, showSettingsInitially: true, animated: true) + } + + func spaceExploreRoomCoordinatorDidAddRoom(_ viewModel: SpaceExploreRoomCoordinatorType, inviteTo item: SpaceExploreRoomListItemViewData) { + self.pushInviteScreen(forRoomWithId: item.childInfo.childRoomId) + } } // MARK: - ShowSpaceChildRoomDetailCoordinator @@ -260,6 +280,7 @@ extension ExploreRoomCoordinator: UIAdaptivePresentationControllerDelegate { } +// MARK: - RoomViewControllerDelegate extension ExploreRoomCoordinator: RoomViewControllerDelegate { func roomViewControllerShowRoomDetails(_ roomViewController: RoomViewController) { // TODO: @@ -348,3 +369,17 @@ extension ExploreRoomCoordinator: RoomViewControllerDelegate { } } + +// MARK: - ContactsPickerCoordinatorDelegate +extension ExploreRoomCoordinator: ContactsPickerCoordinatorDelegate { + func contactsPickerCoordinatorDidStartLoading(_ coordinator: ContactsPickerCoordinatorType) { + } + + func contactsPickerCoordinatorDidEndLoading(_ coordinator: ContactsPickerCoordinatorType) { + } + + func contactsPickerCoordinatorDidClose(_ coordinator: ContactsPickerCoordinatorType) { + childCoordinators.removeLast() + } + +}