Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SP3.2 long press on rooms in space room lists #5702

3 changes: 3 additions & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"add" = "Add";
"ok" = "OK";
"error" = "Error";
"suggest" = "Suggest";

// Call Bar
"callbar_only_single_active" = "Tap to return to the call (%@)";
Expand Down Expand Up @@ -1861,6 +1862,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.";
Expand Down
5 changes: 5 additions & 0 deletions Riot/Generated/Storyboards.swift
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ internal enum StoryboardScene {

internal static let initialScene = InitialSceneType<Riot.SpaceMenuViewController>(storyboard: SpaceMenuViewController.self)
}
internal enum SpaceRoomPreviewViewController: StoryboardType {
internal static let storyboardName = "SpaceRoomPreviewViewController"

internal static let initialScene = InitialSceneType<Riot.SpaceRoomPreviewViewController>(storyboard: SpaceRoomPreviewViewController.self)
}
internal enum TemplateScreenViewController: StoryboardType {
internal static let storyboardName = "TemplateScreenViewController"

Expand Down
12 changes: 12 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5775,6 +5775,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")
Expand Down Expand Up @@ -5827,6 +5835,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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ final class SpaceExploreRoomCoordinator: SpaceExploreRoomCoordinatorType {

// MARK: - SpaceExploreRoomViewModelCoordinatorDelegate
extension SpaceExploreRoomCoordinator: SpaceExploreRoomViewModelCoordinatorDelegate {
func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, didSelect item: SpaceExploreRoomListItemViewData, from sourceView: UIView?) {
func spaceExploreRoomViewModel(_ viewModel: SpaceExploreRoomViewModelType, openSettingsOf item: SpaceExploreRoomListItemViewData) {
self.delegate?.spaceExploreRoomCoordinator(self, openSettingsOf: item)
}

func spaceExploreRoomViewModel(_ coordinator: SpaceExploreRoomViewModelType, inviteTo item: SpaceExploreRoomListItemViewData) {
self.delegate?.spaceExploreRoomCoordinator(self, inviteTo: item)
}

func spaceExploreRoomViewModel(_ coordinator: SpaceExploreRoomViewModelType, didSelect item: SpaceExploreRoomListItemViewData, from sourceView: UIView?) {
self.delegate?.spaceExploreRoomCoordinator(self, didSelect: item, from: sourceView)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 spaceExploreRoomCoordinator(_ coordinator: SpaceExploreRoomCoordinatorType, openSettingsOf item: SpaceExploreRoomListItemViewData)
func spaceExploreRoomCoordinator(_ coordinator: SpaceExploreRoomCoordinatorType, inviteTo item: SpaceExploreRoomListItemViewData)
}

/// `SpaceExploreRoomCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ enum SpaceExploreRoomViewAction {
case loadData
case complete(_ selectedItem: SpaceExploreRoomListItemViewData, _ sourceView: UIView?)
case searchChanged(_ text: String?)
case join
case joinOpenedSpace
case cancel
case addRoom
case inviteTo(_ item: SpaceExploreRoomListItemViewData)
case revertSuggestion(_ item: SpaceExploreRoomListItemViewData)
case settings(_ item: SpaceExploreRoomListItemViewData)
case removeChild(_ item: SpaceExploreRoomListItemViewData)
case join(_ item: SpaceExploreRoomListItemViewData)
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ final class SpaceExploreRoomViewController: UIViewController {
private func setupJoinButton(canJoin: Bool) {
if canJoin {
let joinButtonItem = MXKBarButtonItem(title: VectorL10n.join, style: .done) { [weak self] in
self?.viewModel.process(viewAction: .join)
self?.viewModel.process(viewAction: .joinOpenedSpace)
}

self.navigationItem.rightBarButtonItem = joinButtonItem
Expand Down Expand Up @@ -272,6 +272,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
private var nextBatch: String?
private var rootSpaceChildInfo: MXSpaceChildInfo?

private var spaceRoom: MXRoom?
private var powerLevels: MXRoomPowerLevels?
private var powerLevelOfCurrentUser: Int?

private var canJoin: Bool = false {
didSet {
self.update(viewState: .canJoin(self.canJoin))
}
}

private var itemDataList: [SpaceExploreRoomListItemViewData] = [] {
didSet {
self.updateFilteredItemList()
Expand Down Expand Up @@ -104,11 +109,41 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
self.searchKeyword = newText
case .addRoom:
self.coordinatorDelegate?.spaceExploreRoomViewModelDidAddRoom(self)
case .join:
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)
case .joinOpenedSpace:
self.joinSpace()
}
}

@available(iOS 13.0, *)
func contextMenu(for itemData: SpaceExploreRoomListItemViewData) -> UIMenu {
let canSendSpaceStateEvents: Bool
if let powerLevels = self.powerLevels, let powerLevelOfCurrentUser = self.powerLevelOfCurrentUser {
let minimumPowerLevel = powerLevels.minimumPowerLevel(forNotifications: kMXEventTypeStringRoomJoinRules, defaultPower: powerLevels.stateDefault)
canSendSpaceStateEvents = powerLevelOfCurrentUser >= 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() {
Expand All @@ -124,6 +159,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.powerLevelOfCurrentUser = self.powerLevels?.powerLevelOfUser(withUserID: self.session.myUserId)

}
}

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
Expand Down Expand Up @@ -191,6 +235,156 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
})
}

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.hasMore))
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.hasMore))
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.hasMore))
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
}

private func joinSpace() {
self.update(viewState: .loading)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -36,4 +38,6 @@ protocol SpaceExploreRoomViewModelType {
var showCancelMenuItem: Bool { get }

func process(viewAction: SpaceExploreRoomViewAction)
@available(iOS 13.0, *)
func contextMenu(for itemData: SpaceExploreRoomListItemViewData) -> UIMenu
}
Loading