Update the SDK adopting new power levels APIs. (#2545)

* Update the SDK adopting new power levels APIs.

* Fix test compilation error.
This commit is contained in:
Doug
2024-03-08 15:26:15 +00:00
committed by GitHub
parent 2efc1ce671
commit 4a074e4967
14 changed files with 138 additions and 184 deletions

View File

@@ -7120,7 +7120,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 1.1.46;
version = 1.1.47;
};
};
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {

View File

@@ -130,8 +130,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "f0b060274251263c6470d90d5877af2856c0da14",
"version" : "1.1.46"
"revision" : "57931f2aca93dab50cb86352081a6cdd6cdcf61b",
"version" : "1.1.47"
}
},
{

View File

@@ -131,7 +131,7 @@ class RoomRolesAndPermissionsFlowCoordinator: FlowCoordinatorProtocol {
case .editRoles(let role):
stateMachine.tryEvent(.changeRoles, userInfo: role)
case .editPermissions(let group):
stateMachine.tryEvent(.changePermissions, userInfo: (group, RoomPermissions.default))
stateMachine.tryEvent(.changePermissions, userInfo: (group, RoomPermissions(powerLevels: .mock)))
}
}
.store(in: &cancellables)

View File

@@ -2712,21 +2712,21 @@ class RoomProxyMock: RoomProxyProtocol {
return flagAsFavouriteReturnValue
}
}
//MARK: - currentPowerLevelChanges
//MARK: - powerLevels
var currentPowerLevelChangesCallsCount = 0
var currentPowerLevelChangesCalled: Bool {
return currentPowerLevelChangesCallsCount > 0
var powerLevelsCallsCount = 0
var powerLevelsCalled: Bool {
return powerLevelsCallsCount > 0
}
var currentPowerLevelChangesReturnValue: Result<RoomPowerLevelChanges, RoomProxyError>!
var currentPowerLevelChangesClosure: (() async -> Result<RoomPowerLevelChanges, RoomProxyError>)?
var powerLevelsReturnValue: Result<RoomPowerLevels, RoomProxyError>!
var powerLevelsClosure: (() async -> Result<RoomPowerLevels, RoomProxyError>)?
func currentPowerLevelChanges() async -> Result<RoomPowerLevelChanges, RoomProxyError> {
currentPowerLevelChangesCallsCount += 1
if let currentPowerLevelChangesClosure = currentPowerLevelChangesClosure {
return await currentPowerLevelChangesClosure()
func powerLevels() async -> Result<RoomPowerLevels, RoomProxyError> {
powerLevelsCallsCount += 1
if let powerLevelsClosure = powerLevelsClosure {
return await powerLevelsClosure()
} else {
return currentPowerLevelChangesReturnValue
return powerLevelsReturnValue
}
}
//MARK: - applyPowerLevelChanges

View File

@@ -88,7 +88,7 @@ extension RoomProxyMock {
underlyingIsFavourite = false
flagAsFavouriteReturnValue = .success(())
currentPowerLevelChangesReturnValue = .success(.init())
powerLevelsReturnValue = .success(.mock)
applyPowerLevelChangesReturnValue = .success(())
updatePowerLevelsForUsersReturnValue = .success(())
canUserUserIDSendStateEventClosure = { [weak self] userID, _ in

View File

@@ -31,7 +31,7 @@ struct RoomChangePermissionsScreenViewState: BindableState {
/// Whether or not there are and changes to be saved.
var hasChanges: Bool {
bindings.settings.contains { currentPermissions[keyPath: $0.keyPath] ?? RoomPermissions.defaultValue(for: $0.keyPath) != $0.value }
bindings.settings.contains { currentPermissions[keyPath: $0.keyPath] != $0.value }
}
}
@@ -61,42 +61,42 @@ extension RoomChangePermissionsScreenViewState {
switch group {
case .roomDetails:
let settings = [
RoomPermissionsSetting(keyPath: \.roomName,
value: currentPermissions.roomName ?? RoomPermissions.defaultValue(for: \.roomName),
title: L10n.screenRoomChangePermissionsRoomName),
RoomPermissionsSetting(keyPath: \.roomAvatar,
value: currentPermissions.roomAvatar ?? RoomPermissions.defaultValue(for: \.roomAvatar),
title: L10n.screenRoomChangePermissionsRoomAvatar),
RoomPermissionsSetting(keyPath: \.roomTopic,
value: currentPermissions.roomTopic ?? RoomPermissions.defaultValue(for: \.roomTopic),
title: L10n.screenRoomChangePermissionsRoomTopic)
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsRoomName,
value: currentPermissions.roomName,
keyPath: \.roomName),
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsRoomAvatar,
value: currentPermissions.roomAvatar,
keyPath: \.roomAvatar),
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsRoomTopic,
value: currentPermissions.roomTopic,
keyPath: \.roomTopic)
]
self.init(title: L10n.screenRoomChangePermissionsRoomDetails, currentPermissions: currentPermissions, bindings: .init(settings: settings))
case .messagesAndContent:
let settings = [
RoomPermissionsSetting(keyPath: \.eventsDefault,
value: currentPermissions.eventsDefault ?? RoomPermissions.defaultValue(for: \.eventsDefault),
title: L10n.screenRoomChangePermissionsSendMessages),
RoomPermissionsSetting(keyPath: \.redact,
value: currentPermissions.redact ?? RoomPermissions.defaultValue(for: \.redact),
title: L10n.screenRoomChangePermissionsDeleteMessages)
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsSendMessages,
value: currentPermissions.eventsDefault,
keyPath: \.eventsDefault),
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsDeleteMessages,
value: currentPermissions.redact,
keyPath: \.redact)
]
self.init(title: L10n.screenRoomChangePermissionsMessagesAndContent, currentPermissions: currentPermissions, bindings: .init(settings: settings))
case .memberModeration:
let settings = [
RoomPermissionsSetting(keyPath: \.invite,
value: currentPermissions.invite ?? RoomPermissions.defaultValue(for: \.invite),
title: L10n.screenRoomChangePermissionsInvitePeople),
RoomPermissionsSetting(keyPath: \.kick,
value: currentPermissions.kick ?? RoomPermissions.defaultValue(for: \.kick),
title: L10n.screenRoomChangePermissionsRemovePeople),
RoomPermissionsSetting(keyPath: \.ban,
value: currentPermissions.ban ?? RoomPermissions.defaultValue(for: \.ban),
title: L10n.screenRoomChangePermissionsBanPeople)
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsInvitePeople,
value: currentPermissions.invite,
keyPath: \.invite),
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsRemovePeople,
value: currentPermissions.kick,
keyPath: \.kick),
RoomPermissionsSetting(title: L10n.screenRoomChangePermissionsBanPeople,
value: currentPermissions.ban,
keyPath: \.ban)
]
self.init(title: L10n.screenRoomChangePermissionsMemberModeration, currentPermissions: currentPermissions, bindings: .init(settings: settings))

View File

@@ -15,6 +15,7 @@
//
import Combine
import MatrixRustSDK
import SwiftUI
typealias RoomChangePermissionsScreenViewModelType = StateStoreViewModel<RoomChangePermissionsScreenViewState, RoomChangePermissionsScreenViewAction>
@@ -62,12 +63,12 @@ class RoomChangePermissionsScreenViewModel: RoomChangePermissionsScreenViewModel
hideLoadingIndicator()
}
var updatedPermissions = RoomPermissions()
var changes = RoomPowerLevelChanges()
for setting in state.bindings.settings {
updatedPermissions[keyPath: setting.keyPath] = setting.value
changes[keyPath: setting.rustKeyPath] = setting.value.rustPowerLevel
}
switch await roomProxy.applyPowerLevelChanges(updatedPermissions.makePowerLevelChanges()) {
switch await roomProxy.applyPowerLevelChanges(changes) {
case .success:
MXLog.info("Success")
case .failure:
@@ -75,9 +76,9 @@ class RoomChangePermissionsScreenViewModel: RoomChangePermissionsScreenViewModel
return
}
switch await roomProxy.currentPowerLevelChanges() {
case .success(let powerLevelChanges):
state.currentPermissions = .init(powerLevelChanges: powerLevelChanges)
switch await roomProxy.powerLevels() {
case .success(let powerLevels):
state.currentPermissions = .init(powerLevels: powerLevels)
case .failure:
context.alertInfo = AlertInfo(id: .generic)
return

View File

@@ -75,7 +75,7 @@ struct RoomChangePermissionsScreen_Previews: PreviewProvider, TestablePreview {
}
static func makeViewModel(group: RoomRolesAndPermissionsScreenPermissionsGroup) -> RoomChangePermissionsScreenViewModel {
RoomChangePermissionsScreenViewModel(currentPermissions: .default,
RoomChangePermissionsScreenViewModel(currentPermissions: .init(powerLevels: .mock),
group: group,
roomProxy: RoomProxyMock(with: .init()),
userIndicatorController: UserIndicatorControllerMock())

View File

@@ -18,12 +18,14 @@ import Foundation
import MatrixRustSDK
struct RoomPermissionsSetting: Identifiable {
var id: KeyPath<RoomPermissions, RoomMemberDetails.Role?> { keyPath }
var id: KeyPath<RoomPermissions, RoomMemberDetails.Role> { keyPath }
let keyPath: WritableKeyPath<RoomPermissions, RoomMemberDetails.Role?>
var value: RoomMemberDetails.Role
/// The title of this setting.
let title: String
/// The selected role of this setting.
var value: RoomMemberDetails.Role
/// All of the available roles that this setting can be configured with.
var allValues: [(title: String, tag: RoomMemberDetails.Role)] {
[
(title: L10n.screenRoomChangePermissionsAdministrators, tag: .administrator),
@@ -31,87 +33,63 @@ struct RoomPermissionsSetting: Identifiable {
(title: L10n.screenRoomChangePermissionsEveryone, tag: .user)
]
}
/// The `RoomPermissions` property that this setting is for.
let keyPath: KeyPath<RoomPermissions, RoomMemberDetails.Role>
/// The `RoomPowerLevelChanges` property that this setting is saved into.
var rustKeyPath: WritableKeyPath<RoomPowerLevelChanges, Int64?> {
switch keyPath {
case \.ban: \.ban
case \.invite: \.invite
case \.kick: \.kick
case \.redact: \.redact
case \.eventsDefault: \.eventsDefault
case \.stateDefault: \.stateDefault
case \.usersDefault: \.usersDefault
case \.roomName: \.roomName
case \.roomAvatar: \.roomAvatar
case \.roomTopic: \.roomTopic
default: fatalError("Unexpected key path: \(keyPath)")
}
}
}
struct RoomPermissions {
/// The level required to ban a user.
var ban: RoomMemberDetails.Role?
var ban: RoomMemberDetails.Role
/// The level required to invite a user.
var invite: RoomMemberDetails.Role?
var invite: RoomMemberDetails.Role
/// The level required to kick a user.
var kick: RoomMemberDetails.Role?
var kick: RoomMemberDetails.Role
/// The level required to redact an event.
var redact: RoomMemberDetails.Role?
var redact: RoomMemberDetails.Role
/// The default level required to send message events.
var eventsDefault: RoomMemberDetails.Role?
var eventsDefault: RoomMemberDetails.Role
/// The default level required to send state events.
var stateDefault: RoomMemberDetails.Role?
var stateDefault: RoomMemberDetails.Role
/// The default power level for every user in the room.
var usersDefault: RoomMemberDetails.Role?
var usersDefault: RoomMemberDetails.Role
/// The level required to change the room's name.
var roomName: RoomMemberDetails.Role?
var roomName: RoomMemberDetails.Role
/// The level required to change the room's avatar.
var roomAvatar: RoomMemberDetails.Role?
var roomAvatar: RoomMemberDetails.Role
/// The level required to change the room's topic.
var roomTopic: RoomMemberDetails.Role?
var roomTopic: RoomMemberDetails.Role
}
extension RoomPermissions {
/// Returns the default value for a particular permission.
static func defaultValue(for keyPath: KeyPath<RoomPermissions, RoomMemberDetails.Role?>) -> RoomMemberDetails.Role {
switch keyPath {
case \.ban: .moderator
case \.invite: .user
case \.kick: .moderator
case \.redact: .moderator
case \.eventsDefault: .user
case \.stateDefault: .moderator
case \.usersDefault: .user
case \.roomName: .moderator
case \.roomAvatar: .moderator
case \.roomTopic: .moderator
default: fatalError("Unexpected key path: \(keyPath)")
}
}
/// Constructs a set of permissions using the default values.
static var `default`: RoomPermissions {
RoomPermissions(ban: defaultValue(for: \.ban),
invite: defaultValue(for: \.invite),
kick: defaultValue(for: \.kick),
redact: defaultValue(for: \.redact),
eventsDefault: defaultValue(for: \.eventsDefault),
stateDefault: defaultValue(for: \.stateDefault),
usersDefault: defaultValue(for: \.usersDefault),
roomName: defaultValue(for: \.roomName),
roomAvatar: defaultValue(for: \.roomAvatar),
roomTopic: defaultValue(for: \.roomTopic))
}
init(powerLevelChanges: RoomPowerLevelChanges) {
ban = powerLevelChanges.ban.map(RoomMemberDetails.Role.init)
invite = powerLevelChanges.invite.map(RoomMemberDetails.Role.init)
kick = powerLevelChanges.kick.map(RoomMemberDetails.Role.init)
redact = powerLevelChanges.redact.map(RoomMemberDetails.Role.init)
eventsDefault = powerLevelChanges.eventsDefault.map(RoomMemberDetails.Role.init)
stateDefault = powerLevelChanges.stateDefault.map(RoomMemberDetails.Role.init)
usersDefault = powerLevelChanges.usersDefault.map(RoomMemberDetails.Role.init)
roomName = powerLevelChanges.roomName.map(RoomMemberDetails.Role.init)
roomAvatar = powerLevelChanges.roomAvatar.map(RoomMemberDetails.Role.init)
roomTopic = powerLevelChanges.roomTopic.map(RoomMemberDetails.Role.init)
}
func makePowerLevelChanges() -> RoomPowerLevelChanges {
RoomPowerLevelChanges(ban: ban?.rustPowerLevel,
invite: invite?.rustPowerLevel,
kick: kick?.rustPowerLevel,
redact: redact?.rustPowerLevel,
eventsDefault: eventsDefault?.rustPowerLevel,
stateDefault: stateDefault?.rustPowerLevel,
usersDefault: usersDefault?.rustPowerLevel,
roomName: roomName?.rustPowerLevel,
roomAvatar: roomAvatar?.rustPowerLevel,
roomTopic: roomTopic?.rustPowerLevel)
/// Create permissions from the room's power levels.
init(powerLevels: RoomPowerLevels) {
ban = RoomMemberDetails.Role(rustPowerLevel: powerLevels.ban)
invite = RoomMemberDetails.Role(rustPowerLevel: powerLevels.invite)
kick = RoomMemberDetails.Role(rustPowerLevel: powerLevels.kick)
redact = RoomMemberDetails.Role(rustPowerLevel: powerLevels.redact)
eventsDefault = RoomMemberDetails.Role(rustPowerLevel: powerLevels.eventsDefault)
stateDefault = RoomMemberDetails.Role(rustPowerLevel: powerLevels.stateDefault)
usersDefault = RoomMemberDetails.Role(rustPowerLevel: powerLevels.usersDefault)
roomName = RoomMemberDetails.Role(rustPowerLevel: powerLevels.roomName)
roomAvatar = RoomMemberDetails.Role(rustPowerLevel: powerLevels.roomAvatar)
roomTopic = RoomMemberDetails.Role(rustPowerLevel: powerLevels.roomTopic)
}
}
@@ -135,3 +113,18 @@ extension RoomMemberDetails.Role {
suggestedPowerLevelForRole(role: rustRole)
}
}
extension RoomPowerLevels {
static var mock: RoomPowerLevels {
RoomPowerLevels(ban: 50,
invite: 0,
kick: 50,
redact: 50,
eventsDefault: 0,
stateDefault: 50,
usersDefault: 0,
roomName: 50,
roomAvatar: 50,
roomTopic: 50)
}
}

View File

@@ -373,9 +373,9 @@ class RoomProxy: RoomProxyProtocol {
// MARK: - Power Levels
func currentPowerLevelChanges() async -> Result<RoomPowerLevelChanges, RoomProxyError> {
func powerLevels() async -> Result<RoomPowerLevels, RoomProxyError> {
do {
return try await .success(room.buildPowerLevelChangesFromCurrent())
return try await .success(room.getPowerLevels())
} catch {
MXLog.error("Failed building the current power level settings: \(error)")
return .failure(.failedCheckingPermission)

View File

@@ -115,7 +115,7 @@ protocol RoomProxyProtocol {
// MARK: - Power Levels
func currentPowerLevelChanges() async -> Result<RoomPowerLevelChanges, RoomProxyError>
func powerLevels() async -> Result<RoomPowerLevels, RoomProxyError>
func applyPowerLevelChanges(_ changes: RoomPowerLevelChanges) async -> Result<Void, RoomProxyError>
func updatePowerLevelsForUsers(_ updates: [(userID: String, powerLevel: Int64)]) async -> Result<Void, RoomProxyError>
func canUser(userID: String, sendStateEvent event: StateEventType) async -> Result<Bool, RoomProxyError>

View File

@@ -29,7 +29,7 @@ class RoomChangePermissionsScreenViewModelTests: XCTestCase {
override func setUp() {
roomProxy = RoomProxyMock(with: .init())
viewModel = RoomChangePermissionsScreenViewModel(currentPermissions: .init(),
viewModel = RoomChangePermissionsScreenViewModel(currentPermissions: .init(powerLevels: .mock),
group: .roomDetails,
roomProxy: roomProxy,
userIndicatorController: UserIndicatorControllerMock())
@@ -45,7 +45,7 @@ class RoomChangePermissionsScreenViewModelTests: XCTestCase {
XCTAssertFalse(context.viewState.hasChanges)
// When updating a setting.
let setting = RoomPermissionsSetting(keyPath: \.roomAvatar, value: .user, title: "")
let setting = RoomPermissionsSetting(title: "", value: .user, keyPath: \.roomAvatar)
context.settings[index] = setting
// Then the setting should update and the changes should be flagged.
@@ -59,7 +59,7 @@ class RoomChangePermissionsScreenViewModelTests: XCTestCase {
XCTFail("There should be a setting for the room avatar.")
return
}
context.settings[index] = RoomPermissionsSetting(keyPath: \.roomAvatar, value: .user, title: "")
context.settings[index] = RoomPermissionsSetting(title: "", value: .user, keyPath: \.roomAvatar)
XCTAssertEqual(context.settings[index].value, .user)
XCTAssertTrue(context.viewState.hasChanges)

View File

@@ -20,72 +20,32 @@ import XCTest
@testable import ElementX
class RoomPermissionsTests: XCTestCase {
func testEmptyFromRust() {
// Given an empty set of power level changes.
let powerLevelChanges = RoomPowerLevelChanges()
func testFromRust() {
// Given a set of power level changes with various values.
let powerLevels = RoomPowerLevels(ban: 100,
invite: 100,
kick: 100,
redact: 50,
eventsDefault: 50,
stateDefault: 50,
usersDefault: 0,
roomName: 0,
roomAvatar: 0,
roomTopic: 0)
// When creating room permissions from them.
let permissions = RoomPermissions(powerLevelChanges: powerLevelChanges)
let permissions = RoomPermissions(powerLevels: powerLevels)
// Then none of the permissions should be set.
XCTAssertNil(permissions.ban)
XCTAssertNil(permissions.invite)
XCTAssertNil(permissions.kick)
XCTAssertNil(permissions.redact)
XCTAssertNil(permissions.eventsDefault)
XCTAssertNil(permissions.stateDefault)
XCTAssertNil(permissions.usersDefault)
XCTAssertNil(permissions.roomName)
XCTAssertNil(permissions.roomAvatar)
XCTAssertNil(permissions.roomTopic)
}
func testCompleteFromRust() {
// Given a set of power level changes with all the values set to 100.
let powerLevelChanges = RoomPowerLevelChanges(ban: 100,
invite: 100,
kick: 100,
redact: 100,
eventsDefault: 100,
stateDefault: 100,
usersDefault: 100,
roomName: 100,
roomAvatar: 100,
roomTopic: 100)
// When creating room permissions from them.
let permissions = RoomPermissions(powerLevelChanges: powerLevelChanges)
// Then all of the permissions should be for an administrator.
// Then the permissions should be created with values mapped to the correct role.
XCTAssertEqual(permissions.ban, .administrator)
XCTAssertEqual(permissions.invite, .administrator)
XCTAssertEqual(permissions.kick, .administrator)
XCTAssertEqual(permissions.redact, .administrator)
XCTAssertEqual(permissions.eventsDefault, .administrator)
XCTAssertEqual(permissions.stateDefault, .administrator)
XCTAssertEqual(permissions.usersDefault, .administrator)
XCTAssertEqual(permissions.roomName, .administrator)
XCTAssertEqual(permissions.roomAvatar, .administrator)
XCTAssertEqual(permissions.roomTopic, .administrator)
}
func testToRust() {
// Given a set of permissions where on some of the values have been set.
let permissions = RoomPermissions(roomName: .administrator, roomAvatar: .administrator, roomTopic: .administrator)
// When creating power level changes from them.
let powerLevelChanges = permissions.makePowerLevelChanges()
// Then only the permissions that were set should be included.
XCTAssertNil(powerLevelChanges.ban, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.invite, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.kick, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.redact, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.eventsDefault, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.stateDefault, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertNil(powerLevelChanges.usersDefault, "Unset values should be nil for Rust to merge with the current value.")
XCTAssertEqual(powerLevelChanges.roomName, 100)
XCTAssertEqual(powerLevelChanges.roomAvatar, 100)
XCTAssertEqual(powerLevelChanges.roomTopic, 100)
XCTAssertEqual(permissions.redact, .moderator)
XCTAssertEqual(permissions.eventsDefault, .moderator)
XCTAssertEqual(permissions.stateDefault, .moderator)
XCTAssertEqual(permissions.usersDefault, .user)
XCTAssertEqual(permissions.roomName, .user)
XCTAssertEqual(permissions.roomAvatar, .user)
XCTAssertEqual(permissions.roomTopic, .user)
}
}

View File

@@ -48,7 +48,7 @@ packages:
# Element/Matrix dependencies
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.1.46
exactVersion: 1.1.47
# path: ../matrix-rust-sdk
Compound:
url: https://github.com/element-hq/compound-ios