From 5a87fb4f928d4534a3b4b0ae4871d31a588df65c Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:52:55 +0200 Subject: [PATCH] Restore permissions to creator and display them as owners in the list (#4369) * restore permissions to creator and display them as owners in the list * improved the code to use actually 5 roles in the app to distinguish a real creator from an owner --- .../Mocks/Generated/GeneratedMocks.swift | 134 +++++++++--------- .../Sources/Mocks/JoinedRoomProxyMock.swift | 8 +- .../Sources/Mocks/RoomMemberProxyMock.swift | 16 +++ .../Mocks/RoomPowerLevelsProxyMock.swift | 6 +- .../RoomChangeRolesScreenModels.swift | 3 +- .../RoomChangeRolesScreenViewModel.swift | 2 +- .../View/RoomChangeRolesScreenSection.swift | 4 +- .../RoomDetailsScreenViewModel.swift | 2 +- .../View/RoomMembersListScreen.swift | 2 + .../RoomMembersListScreenMemberCell.swift | 2 + ...omRolesAndPermissionsScreenViewModel.swift | 3 +- .../Helpers/RoomModerationRole.swift | 3 +- .../Room/RoomMember/RoomMemberDetails.swift | 57 ++++++-- .../Services/Room/RoomPermissions.swift | 31 +++- .../Room/RoomPowerLevelProxyProtocol.swift | 5 +- .../Services/Room/RoomPowerLevelsProxy.swift | 14 +- ...ersListScreen.Admin-Members-iPad-en-GB.png | 4 +- ...rsListScreen.Admin-Members-iPad-pseudo.png | 4 +- ...stScreen.Admin-Members-iPhone-16-en-GB.png | 4 +- ...tScreen.Admin-Members-iPhone-16-pseudo.png | 4 +- ...omMembersListScreen.Invites-iPad-en-GB.png | 4 +- ...mMembersListScreen.Invites-iPad-pseudo.png | 4 +- ...bersListScreen.Invites-iPhone-16-en-GB.png | 4 +- ...ersListScreen.Invites-iPhone-16-pseudo.png | 4 +- ...oomMembersListScreen.Member-iPad-en-GB.png | 4 +- ...omMembersListScreen.Member-iPad-pseudo.png | 4 +- ...mbersListScreen.Member-iPhone-16-en-GB.png | 4 +- ...bersListScreen.Member-iPhone-16-pseudo.png | 4 +- .../RoomMembersListScreenViewModelTests.swift | 2 +- 29 files changed, 205 insertions(+), 137 deletions(-) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index baf1b4b1a..9648b4877 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -13435,76 +13435,6 @@ class RoomPowerLevelsProxyMock: RoomPowerLevelsProxyProtocol, @unchecked Sendabl var underlyingValues: RoomPowerLevelsValues! var userPowerLevels: [String: Int64] = [:] - //MARK: - suggestedRole - - var suggestedRoleForUserUnderlyingCallsCount = 0 - var suggestedRoleForUserCallsCount: Int { - get { - if Thread.isMainThread { - return suggestedRoleForUserUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = suggestedRoleForUserUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - suggestedRoleForUserUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - suggestedRoleForUserUnderlyingCallsCount = newValue - } - } - } - } - var suggestedRoleForUserCalled: Bool { - return suggestedRoleForUserCallsCount > 0 - } - var suggestedRoleForUserReceivedUserID: String? - var suggestedRoleForUserReceivedInvocations: [String] = [] - - var suggestedRoleForUserUnderlyingReturnValue: RoomMemberRole! - var suggestedRoleForUserReturnValue: RoomMemberRole! { - get { - if Thread.isMainThread { - return suggestedRoleForUserUnderlyingReturnValue - } else { - var returnValue: RoomMemberRole? = nil - DispatchQueue.main.sync { - returnValue = suggestedRoleForUserUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - suggestedRoleForUserUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - suggestedRoleForUserUnderlyingReturnValue = newValue - } - } - } - } - var suggestedRoleForUserClosure: ((String) -> RoomMemberRole)? - - func suggestedRole(forUser userID: String) -> RoomMemberRole { - suggestedRoleForUserCallsCount += 1 - suggestedRoleForUserReceivedUserID = userID - DispatchQueue.main.async { - self.suggestedRoleForUserReceivedInvocations.append(userID) - } - if let suggestedRoleForUserClosure = suggestedRoleForUserClosure { - return suggestedRoleForUserClosure(userID) - } else { - return suggestedRoleForUserReturnValue - } - } //MARK: - canOwnUser var canOwnUserSendMessageUnderlyingCallsCount = 0 @@ -14157,6 +14087,70 @@ class RoomPowerLevelsProxyMock: RoomPowerLevelsProxyProtocol, @unchecked Sendabl return canOwnUserJoinCallReturnValue } } + //MARK: - canOwnUserEditRolesAndPermissions + + var canOwnUserEditRolesAndPermissionsUnderlyingCallsCount = 0 + var canOwnUserEditRolesAndPermissionsCallsCount: Int { + get { + if Thread.isMainThread { + return canOwnUserEditRolesAndPermissionsUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = canOwnUserEditRolesAndPermissionsUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + canOwnUserEditRolesAndPermissionsUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + canOwnUserEditRolesAndPermissionsUnderlyingCallsCount = newValue + } + } + } + } + var canOwnUserEditRolesAndPermissionsCalled: Bool { + return canOwnUserEditRolesAndPermissionsCallsCount > 0 + } + + var canOwnUserEditRolesAndPermissionsUnderlyingReturnValue: Bool! + var canOwnUserEditRolesAndPermissionsReturnValue: Bool! { + get { + if Thread.isMainThread { + return canOwnUserEditRolesAndPermissionsUnderlyingReturnValue + } else { + var returnValue: Bool? = nil + DispatchQueue.main.sync { + returnValue = canOwnUserEditRolesAndPermissionsUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + canOwnUserEditRolesAndPermissionsUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + canOwnUserEditRolesAndPermissionsUnderlyingReturnValue = newValue + } + } + } + } + var canOwnUserEditRolesAndPermissionsClosure: (() -> Bool)? + + func canOwnUserEditRolesAndPermissions() -> Bool { + canOwnUserEditRolesAndPermissionsCallsCount += 1 + if let canOwnUserEditRolesAndPermissionsClosure = canOwnUserEditRolesAndPermissionsClosure { + return canOwnUserEditRolesAndPermissionsClosure() + } else { + return canOwnUserEditRolesAndPermissionsReturnValue + } + } //MARK: - canUser var canUserUserIDSendMessageUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index 132011d4c..9eceb8106 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -116,12 +116,8 @@ extension JoinedRoomProxyMock { self?.membersPublisher.value.first { $0.userID == configuration.ownUserID }?.role ?? .user != .user } - powerLevelsProxyMock.suggestedRoleForUserClosure = { [weak self] userID in - guard let member = self?.membersPublisher.value.first(where: { $0.userID == userID }) else { - return .user - } - - return member.role + powerLevelsProxyMock.canOwnUserEditRolesAndPermissionsClosure = { [weak self] in + self?.membersPublisher.value.first { $0.userID == configuration.ownUserID }?.role.isAdminOrHigher ?? false } powerLevelsReturnValue = .success(powerLevelsProxyMock) diff --git a/ElementX/Sources/Mocks/RoomMemberProxyMock.swift b/ElementX/Sources/Mocks/RoomMemberProxyMock.swift index de9fa1328..ac81dffd8 100644 --- a/ElementX/Sources/Mocks/RoomMemberProxyMock.swift +++ b/ElementX/Sources/Mocks/RoomMemberProxyMock.swift @@ -120,6 +120,22 @@ extension RoomMemberProxyMock { role: .administrator)) } + static var mockCreator: RoomMemberProxyMock { + RoomMemberProxyMock(with: .init(userID: "@creator:matrix.org", + displayName: "God", + membership: .join, + powerLevel: .infinite, + role: .creator)) + } + + static var mockOwner: RoomMemberProxyMock { + RoomMemberProxyMock(with: .init(userID: "@owner:matrix.org", + displayName: "Guinevere", + membership: .join, + powerLevel: .value(150), + role: .administrator)) + } + static var mockModerator: RoomMemberProxyMock { RoomMemberProxyMock(with: .init(userID: "@mod:matrix.org", displayName: "Merlin", diff --git a/ElementX/Sources/Mocks/RoomPowerLevelsProxyMock.swift b/ElementX/Sources/Mocks/RoomPowerLevelsProxyMock.swift index fe8cecce3..84ae3949f 100644 --- a/ElementX/Sources/Mocks/RoomPowerLevelsProxyMock.swift +++ b/ElementX/Sources/Mocks/RoomPowerLevelsProxyMock.swift @@ -18,6 +18,7 @@ struct RoomPowerLevelsProxyMockConfiguration { var canUserTriggerRoomNotification = false var canUserPin = true var canUserJoinCall = true + var canUserEditRoomsAndPermissions = true } extension RoomPowerLevelsProxyMock { @@ -25,9 +26,7 @@ extension RoomPowerLevelsProxyMock { self.init() underlyingValues = RoomPowerLevelsValues.mock - - suggestedRoleForUserReturnValue = .administrator - + canOwnUserSendMessageReturnValue = configuration.canUserSendMessage canOwnUserSendStateEventReturnValue = configuration.canUserSendState canOwnUserInviteReturnValue = configuration.canUserInvite @@ -38,6 +37,7 @@ extension RoomPowerLevelsProxyMock { canOwnUserTriggerRoomNotificationReturnValue = configuration.canUserTriggerRoomNotification canOwnUserPinOrUnpinReturnValue = configuration.canUserPin canOwnUserJoinCallReturnValue = configuration.canUserJoinCall + canOwnUserEditRolesAndPermissionsReturnValue = configuration.canUserEditRoomsAndPermissions canUserUserIDSendMessageReturnValue = .success(configuration.canUserSendMessage) canUserUserIDSendStateEventReturnValue = .success(configuration.canUserSendState) diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenModels.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenModels.swift index 75a2e9e68..52398d7cf 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenModels.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenModels.swift @@ -35,7 +35,8 @@ struct RoomChangeRolesScreenViewState: BindableState { /// The screen's title. var title: String { switch mode { - case .administrator: + case .creator, .owner, .administrator: + // TODO: Handle the creator permissions change L10n.screenRoomChangeRoleAdministratorsTitle case .moderator: L10n.screenRoomChangeRoleModeratorsTitle diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift index b40126479..94a8833ec 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift @@ -64,7 +64,7 @@ class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomCh case .demoteMember(let member): demoteMember(member) case .save: - if state.mode == .administrator, !state.membersToPromote.isEmpty { + if state.mode.isAdminOrHigher, !state.membersToPromote.isEmpty { showPromotionWarning() } else { Task { await save() } diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSection.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSection.swift index f49b8adb9..9fde1dd0a 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSection.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSection.swift @@ -24,7 +24,7 @@ struct RoomChangeRolesScreenSection: View { isSelected: isMemberSelected(member)) { context.send(viewAction: .toggleMember(member)) } - .disabled(member.role == .administrator) + .disabled(member.role.isAdminOrHigher) } } header: { Text(title) @@ -40,6 +40,6 @@ struct RoomChangeRolesScreenSection: View { private func isMemberSelected(_ member: RoomMemberDetails) -> Bool { // We always show administrators as selected, even on the moderators screen. - member.role == .administrator || context.viewState.isMemberSelected(member) + member.role.isAdminOrHigher || context.viewState.isMemberSelected(member) } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 6e7de350a..16e96f0be 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -232,7 +232,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr state.canKickUsers = powerLevels.canOwnUserKick() state.canBanUsers = powerLevels.canOwnUserBan() state.canJoinCall = powerLevels.canOwnUserJoinCall() - state.canEditRolesOrPermissions = powerLevels.suggestedRole(forUser: roomProxy.ownUserID) == .administrator + state.canEditRolesOrPermissions = powerLevels.canOwnUserEditRolesAndPermissions() } } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift index 456263116..6717756d4 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift @@ -168,6 +168,8 @@ struct RoomMembersListScreen_Previews: PreviewProvider, TestablePreview { .mockBob, .mockCharlie, mockAdmin, + .mockCreator, + .mockOwner, .mockModerator ] diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift index 5296f5f3a..0c8437ec8 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift @@ -55,6 +55,8 @@ struct RoomMembersListScreenMemberCell: View { var role: String? { switch listEntry.member.role { + case .creator, .owner: + L10n.screenRoomMemberListRoleOwner case .administrator: L10n.screenRoomMemberListRoleAdministrator case .moderator: diff --git a/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift b/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift index e7b8e13bb..0154d5dec 100644 --- a/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift @@ -84,7 +84,8 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM // MARK: - Members private func updateMembers(_ members: [RoomMemberProxyProtocol]) { - state.administratorCount = members.filter { $0.role == .administrator && $0.isActive }.count + // TODO: Will probably be changed when we implement the owners list + state.administratorCount = members.filter { $0.role.isAdminOrHigher && $0.isActive }.count state.moderatorCount = members.filter { $0.role == .moderator && $0.isActive }.count } diff --git a/ElementX/Sources/Services/Analytics/Helpers/RoomModerationRole.swift b/ElementX/Sources/Services/Analytics/Helpers/RoomModerationRole.swift index e357f55d9..981f69532 100644 --- a/ElementX/Sources/Services/Analytics/Helpers/RoomModerationRole.swift +++ b/ElementX/Sources/Services/Analytics/Helpers/RoomModerationRole.swift @@ -10,7 +10,8 @@ import AnalyticsEvents extension AnalyticsEvent.RoomModeration.Role { init(role: RoomMemberDetails.Role) { switch role { - case .administrator: + case .administrator, .creator, .owner: + // This probably needs to be updates self = .Administrator case .moderator: self = .Moderator diff --git a/ElementX/Sources/Services/Room/RoomMember/RoomMemberDetails.swift b/ElementX/Sources/Services/Room/RoomMember/RoomMemberDetails.swift index 435c3bdd3..1aa565023 100644 --- a/ElementX/Sources/Services/Room/RoomMember/RoomMemberDetails.swift +++ b/ElementX/Sources/Services/Room/RoomMember/RoomMemberDetails.swift @@ -19,7 +19,19 @@ struct RoomMemberDetails: Identifiable, Hashable { var isBanned: Bool var isActive: Bool - enum Role { case administrator, moderator, user } + enum Role { + /// Creator of the room, PL infinite + case creator + /// Same power of an admin, but they can also upgrade the room, PL 150 onwards + case owner + /// Able to edit room settings and perform any action aside from room upgrading PL 100...149 + case administrator + /// Able to perform room moderation actions PL 50...99 + case moderator + /// Default role PL 0...49 + case user + } + let role: Role let powerLevel: RoomPowerLevel @@ -39,18 +51,47 @@ extension RoomMemberDetails { isInvited = proxy.membership == .invite isIgnored = proxy.isIgnored isBanned = proxy.membership == .ban - role = .init(proxy.role) + role = .init(proxy.role, powerLevel: proxy.powerLevel) powerLevel = proxy.powerLevel } } extension RoomMemberDetails.Role { - init(_ role: RoomMemberRole) { - self = switch role { - // TODO: Implement creator role - case .creator, .administrator: .administrator - case .moderator: .moderator - case .user: .user + init(_ role: RoomMemberRole, powerLevel: RoomPowerLevel) { + switch role { + case .creator: + self = .creator + case .administrator: + switch powerLevel { + case .value(let value): + self = value >= 150 ? .owner : .administrator + default: + fatalError("Impossible") + } + case .moderator: + self = .moderator + case .user: + self = .user + } + } + + var isAdminOrHigher: Bool { + switch self { + case .administrator, .creator, .owner: + return true + case .moderator, .user: + return false + } + } +} + +extension RoomMemberRole { + var isAdminOrHigher: Bool { + switch self { + case .administrator, .creator: + return true + case .moderator, .user: + return false } } } diff --git a/ElementX/Sources/Services/Room/RoomPermissions.swift b/ElementX/Sources/Services/Room/RoomPermissions.swift index 24042c3b2..3dfde378e 100644 --- a/ElementX/Sources/Services/Room/RoomPermissions.swift +++ b/ElementX/Sources/Services/Room/RoomPermissions.swift @@ -86,17 +86,34 @@ extension RoomPermissions { extension RoomMemberDetails.Role { init(powerLevelValue: Int64) { + // Also this is not great, and should be handled by a `suggestedRoleForPowerLevelValue` function from the SDK + guard powerLevelValue < 150 else { + self = .owner + return + } + do { - try self.init(suggestedRoleForPowerLevel(powerLevel: .value(value: powerLevelValue))) + switch try suggestedRoleForPowerLevel(powerLevel: .value(value: powerLevelValue)) { + case .administrator: + self = .administrator + case .creator: + fatalError("Impossible") + case .moderator: + self = .moderator + case .user: + self = .user + } } catch { MXLog.error("Falied to convert power level value to role: \(error)") - self.init(.user) + self = .user } } var rustRole: RoomMemberRole { switch self { - case .administrator: + case .creator: + .creator + case .administrator, .owner: .administrator case .moderator: .moderator @@ -108,11 +125,15 @@ extension RoomMemberDetails.Role { /// To be used when setting the power level of a user to get the suggested equivalent power level value for that specific role /// NOTE: Do not use for comparison, use the true power level instead. var powerLevelValue: Int64 { + guard self != .owner else { + // Would be better if the SDK would return this, maybe a `suggestedPowerLevelValueForRole` function would solve the problem + return 150 + } + do { switch try suggestedPowerLevelForRole(role: rustRole) { case .infinite: - // Would be better if the SDK would return this, maybe a `suggestedPowerLevelValueForRole` function would solve the problem - return 150 + fatalError("Impossible") case .value(let value): return value } diff --git a/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift index 606a50b60..266d5895b 100644 --- a/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift @@ -11,9 +11,7 @@ import MatrixRustSDK protocol RoomPowerLevelsProxyProtocol { var values: RoomPowerLevelsValues { get } var userPowerLevels: [String: Int64] { get } - - func suggestedRole(forUser userID: String) -> RoomMemberRole - + func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool func canOwnUser(sendStateEvent event: StateEventType) -> Bool func canOwnUserInvite() -> Bool @@ -24,6 +22,7 @@ protocol RoomPowerLevelsProxyProtocol { func canOwnUserTriggerRoomNotification() -> Bool func canOwnUserPinOrUnpin() -> Bool func canOwnUserJoinCall() -> Bool + func canOwnUserEditRolesAndPermissions() -> Bool func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result func canUser(userID: String, sendStateEvent event: StateEventType) -> Result diff --git a/ElementX/Sources/Services/Room/RoomPowerLevelsProxy.swift b/ElementX/Sources/Services/Room/RoomPowerLevelsProxy.swift index c2dfe1920..07c6ce902 100644 --- a/ElementX/Sources/Services/Room/RoomPowerLevelsProxy.swift +++ b/ElementX/Sources/Services/Room/RoomPowerLevelsProxy.swift @@ -26,16 +26,6 @@ struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol { powerLevels.userPowerLevels() } - func suggestedRole(forUser userID: String) -> RoomMemberRole { - do { - let powerLevelValue = powerLevels.userPowerLevels()[userID] ?? values.usersDefault - return try suggestedRoleForPowerLevel(powerLevel: .value(value: powerLevelValue)) - } catch { - MXLog.error("Falied to get suggested role for user: \(error)") - return .user - } - } - func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool { powerLevels.canOwnUserSendMessage(message: messageType) } @@ -76,6 +66,10 @@ struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol { powerLevels.canOwnUserSendState(stateEvent: .callMember) } + func canOwnUserEditRolesAndPermissions() -> Bool { + powerLevels.canOwnUserSendState(stateEvent: .roomPowerLevels) + } + func canUser(userID: String, sendMessage messageType: MessageLikeEventType) -> Result { do { return try .success(powerLevels.canUserSendMessage(userId: userID, message: messageType)) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-en-GB.png index 8c1ab0ef4..f0504d37c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe572408dfab096b40fe832f3af04825604aa128f672feaac239e7a4f7b10d73 -size 134463 +oid sha256:de7dd0a25278cb3d850bb17745eabf73c82bb8de714de3176176ef0205cfda26 +size 155431 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-pseudo.png index d6587a28c..08cb3c8f8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11163c9d94a5c0e3d1f1d725eb2c886cb243d4e59564c80aebdf5325bd335a54 -size 143380 +oid sha256:a720efc67edbb7eb11b0a2f9bd85850729937f99bb077b0d08f7598484d9c6a7 +size 167197 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-en-GB.png index 271646a83..f02e34887 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a37767187891e271c7ebb6ec62f8251022d7f07eefdebd7a3b883f95b6a6c877 -size 84829 +oid sha256:6ce76f7eb6367b8ac122d77017907b08052a2d1bc73af12fcf5c5826a2de83f4 +size 101372 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-pseudo.png index 3739fb79b..38f1f509c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Admin-Members-iPhone-16-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1db62e88774b134efce48a06df934aab020cff16f1e0c1c2729edc036116a59c -size 94434 +oid sha256:36f0da3b02ad88aef6045288f2520866de1ca2fece50c302a3e79a97c414dab1 +size 112674 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-en-GB.png index 9ed26cff4..2913e5cba 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21ded3dc1828d99325b5bcf85eda0be6daee71fc6571344cccdffa3d5d93437e -size 137744 +oid sha256:536c5e08b44cbe6d3b9d1c2e6ec8b32d919f911ad5af09cf59d29a6985092d19 +size 158614 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-pseudo.png index f23c0bf0a..d468e8838 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39ffdb0635318f13f96ee4b0939b1b91804ddae5932e872cd044ca78f8c4b669 -size 145858 +oid sha256:ea481ed68ee357f10b6f1777addad6d927c5de71f75862c7859ead98d6445ece +size 169472 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-en-GB.png index 27af7e9c2..023d1cfee 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f07ca02c19c528cc8de2bc496203dd439452290f2fd71f71af5f18d3988de12d -size 87192 +oid sha256:d163845228de4646f5ed876b5c44850a9f24a18a4e3f0d87c206b13e78e3b199 +size 104634 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-pseudo.png index 77c0eb444..fd6557f8a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Invites-iPhone-16-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67f6d58d14feac246ed347c515dcdd4e63fadf51dfab22c6d8332f60467a0f17 -size 97161 +oid sha256:df3c4dee3447725f3dd2cc5665a1ecde0a7816625eab674f39909d5578ebe83b +size 116454 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-en-GB.png index 6265792e1..f0f7d1fff 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adb9a4c8585acb6bd4841723299c662dbb0285b8a81ee7b30042aebc0be8d163 -size 127054 +oid sha256:02637fcc5d6a92beb9bdd84e2ad1018f4021639bb03a159e7e940c5f36ab9093 +size 147956 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-pseudo.png index 23cffe73b..419a9750f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9be1a0c6030ef1ff21c83faf99f304cf755a07bc04294f4ccae05b8365c0485a -size 135050 +oid sha256:c4964d2d39a3e8ede1c817d2b771bd12fa28e4678a9df81b34d509cb922e3f38 +size 158785 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-en-GB.png index 9853277c2..ceb73ba5c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a90905ef38f880e2c67368f9ccfc6e4e811c8c7b69b4cccd39672435abe5f6a5 -size 78999 +oid sha256:0a52e8ed7e8a131fbd5a1453109a7741607a20eb9a06be07bc3a49a1363d05d7 +size 94960 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-pseudo.png index 07f0b183b..c7a6b7f94 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomMembersListScreen.Member-iPhone-16-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e22ae73b0ff3730dce2f6becc460ec5d07be3262cd69d74d7f3f30ca8f8cd23d -size 87347 +oid sha256:b9781146ec5417aac27600db5e77f01002c454c58e3d1905ebf63d0e19710b56 +size 105019 diff --git a/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift b/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift index 6bb05abdb..7ba2dc34d 100644 --- a/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift @@ -212,7 +212,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase { // When tapping on another administrator in the list. deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil } - guard let admin = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .administrator && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else { + guard let admin = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role.isAdminOrHigher && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else { XCTFail("Expected to find another admin.") return }