Element Call permission tweaks (#2187)

* vector-im/element-meta/issues/2230 - Specify `call.member` default power levels when creating a room

* vector-im/element-meta/issues/2229 - Take into account permissions when configuring the room screen call button
This commit is contained in:
Stefan Ceriu
2023-12-06 13:14:23 +02:00
committed by GitHub
parent c7531be44c
commit 6cd25fac50
11 changed files with 78 additions and 9 deletions
@@ -2357,6 +2357,27 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserTriggerRoomNotificationUserIDReturnValue return canUserTriggerRoomNotificationUserIDReturnValue
} }
} }
//MARK: - canUserJoinCall
var canUserJoinCallUserIDCallsCount = 0
var canUserJoinCallUserIDCalled: Bool {
return canUserJoinCallUserIDCallsCount > 0
}
var canUserJoinCallUserIDReceivedUserID: String?
var canUserJoinCallUserIDReceivedInvocations: [String] = []
var canUserJoinCallUserIDReturnValue: Result<Bool, RoomProxyError>!
var canUserJoinCallUserIDClosure: ((String) async -> Result<Bool, RoomProxyError>)?
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> {
canUserJoinCallUserIDCallsCount += 1
canUserJoinCallUserIDReceivedUserID = userID
canUserJoinCallUserIDReceivedInvocations.append(userID)
if let canUserJoinCallUserIDClosure = canUserJoinCallUserIDClosure {
return await canUserJoinCallUserIDClosure(userID)
} else {
return canUserJoinCallUserIDReturnValue
}
}
//MARK: - elementCallWidgetDriver //MARK: - elementCallWidgetDriver
var elementCallWidgetDriverCallsCount = 0 var elementCallWidgetDriverCallsCount = 0
+4 -1
View File
@@ -32,7 +32,6 @@ struct RoomProxyMockConfiguration {
var canonicalAlias: String? var canonicalAlias: String?
var alternativeAliases: [String] = [] var alternativeAliases: [String] = []
var hasUnreadNotifications = Bool.random() var hasUnreadNotifications = Bool.random()
var canUserTriggerRoomNotification = false
var timeline = { var timeline = {
let mock = TimelineProxyMock() let mock = TimelineProxyMock()
@@ -45,6 +44,9 @@ struct RoomProxyMockConfiguration {
var memberForID: RoomMemberProxyMock = .mockMe var memberForID: RoomMemberProxyMock = .mockMe
var ownUserID = "@alice:somewhere.org" var ownUserID = "@alice:somewhere.org"
var canUserTriggerRoomNotification = false
var canUserJoinCall = false
var invitedMembersCount = 100 var invitedMembersCount = 100
var joinedMembersCount = 50 var joinedMembersCount = 50
var activeMembersCount = 25 var activeMembersCount = 25
@@ -94,5 +96,6 @@ extension RoomProxyMock {
getMemberUserIDReturnValue = .success(configuration.memberForID) getMemberUserIDReturnValue = .success(configuration.memberForID)
canUserRedactUserIDReturnValue = .success(false) canUserRedactUserIDReturnValue = .success(false)
canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification) canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification)
canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall)
} }
} }
@@ -129,6 +129,7 @@ struct RoomScreenViewState: BindableState {
var ownUserID: String var ownUserID: String
var canJoinCall = false
var hasOngoingCall = false var hasOngoingCall = false
var bindings: RoomScreenViewStateBindings var bindings: RoomScreenViewStateBindings
@@ -115,6 +115,13 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
// Note: beware if we get to e.g. restore a reply / edit, // Note: beware if we get to e.g. restore a reply / edit,
// maybe we are tracking a non-needed first initial state // maybe we are tracking a non-needed first initial state
trackComposerMode(.default) trackComposerMode(.default)
Task {
let userID = roomProxy.ownUserID
if case let .success(permission) = await roomProxy.canUserJoinCall(userID: userID) {
state.canJoinCall = permission
}
}
} }
// MARK: - Public // MARK: - Public
@@ -162,6 +162,7 @@ struct RoomScreen: View {
if !ProcessInfo.processInfo.isiOSAppOnMac { if !ProcessInfo.processInfo.isiOSAppOnMac {
ToolbarItem(placement: .primaryAction) { ToolbarItem(placement: .primaryAction) {
callButton callButton
.disabled(context.viewState.canJoinCall == false)
} }
} }
} }
@@ -50,6 +50,22 @@ class ClientProxy: ClientProxyProtocol {
let secureBackupController: SecureBackupControllerProtocol let secureBackupController: SecureBackupControllerProtocol
private let roomListRecencyOrderingAllowedEventTypes = ["m.room.message", "m.room.encrypted", "m.sticker"] private let roomListRecencyOrderingAllowedEventTypes = ["m.room.message", "m.room.encrypted", "m.sticker"]
private static var roomCreationPowerLevelOverrides: PowerLevels {
.init(usersDefault: nil,
eventsDefault: nil,
stateDefault: nil,
ban: nil,
kick: nil,
redact: nil,
invite: nil,
notifications: nil,
users: [:],
events: [
"m.call.member": Int32(0),
"org.matrix.msc3401.call.member": Int32(0)
])
}
private var loadCachedAvatarURLTask: Task<Void, Never>? private var loadCachedAvatarURLTask: Task<Void, Never>?
private let userAvatarURLSubject = CurrentValueSubject<URL?, Never>(nil) private let userAvatarURLSubject = CurrentValueSubject<URL?, Never>(nil)
@@ -208,7 +224,15 @@ class ClientProxy: ClientProxyProtocol {
func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result<String, ClientProxyError> { func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result<String, ClientProxyError> {
let result: Result<String, ClientProxyError> = await Task.dispatch(on: clientQueue) { let result: Result<String, ClientProxyError> = await Task.dispatch(on: clientQueue) {
do { do {
let parameters = CreateRoomParameters(name: nil, topic: nil, isEncrypted: true, isDirect: true, visibility: .private, preset: .trustedPrivateChat, invite: [userID], avatar: nil) let parameters = CreateRoomParameters(name: nil,
topic: nil,
isEncrypted: true,
isDirect: true,
visibility: .private,
preset: .trustedPrivateChat,
invite: [userID],
avatar: nil,
powerLevelContentOverride: Self.roomCreationPowerLevelOverrides)
let result = try self.client.createRoom(request: parameters) let result = try self.client.createRoom(request: parameters)
return .success(result) return .success(result)
} catch { } catch {
@@ -229,7 +253,8 @@ class ClientProxy: ClientProxyProtocol {
visibility: isRoomPrivate ? .private : .public, visibility: isRoomPrivate ? .private : .public,
preset: isRoomPrivate ? .privateChat : .publicChat, preset: isRoomPrivate ? .privateChat : .publicChat,
invite: userIDs, invite: userIDs,
avatar: avatarURL?.absoluteString) avatar: avatarURL?.absoluteString,
powerLevelContentOverride: Self.roomCreationPowerLevelOverrides)
let roomID = try self.client.createRoom(request: parameters) let roomID = try self.client.createRoom(request: parameters)
return .success(roomID) return .success(roomID)
} catch { } catch {
@@ -396,6 +396,15 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> {
do {
return try await .success(room.canUserSendState(userId: userID, stateEvent: .callMember))
} catch {
MXLog.error("Failed checking if the user can trigger room notification with error: \(error)")
return .failure(.failedCheckingPermission)
}
}
// MARK: - Element Call // MARK: - Element Call
func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol { func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol {
@@ -114,6 +114,8 @@ protocol RoomProxyProtocol {
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError>
// MARK: - Element Call // MARK: - Element Call
func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol
@@ -66,7 +66,7 @@ final class CompletionSuggestionServiceTests: XCTestCase {
func testUserSuggestonsIncludingAllUsers() async throws { func testUserSuggestonsIncludingAllUsers() async throws {
let alice: RoomMemberProxyMock = .mockAlice let alice: RoomMemberProxyMock = .mockAlice
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe] let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", canUserTriggerRoomNotification: true, members: members)) let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", members: members, canUserTriggerRoomNotification: true))
let service = CompletionSuggestionService(roomProxy: roomProxyMock) let service = CompletionSuggestionService(roomProxy: roomProxyMock)
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
@@ -92,7 +92,7 @@ final class CompletionSuggestionServiceTests: XCTestCase {
let alice: RoomMemberProxyMock = .mockAlice let alice: RoomMemberProxyMock = .mockAlice
let bob: RoomMemberProxyMock = .mockBob let bob: RoomMemberProxyMock = .mockBob
let members: [RoomMemberProxyMock] = [alice, bob, .mockMe] let members: [RoomMemberProxyMock] = [alice, bob, .mockMe]
let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", canUserTriggerRoomNotification: true, members: members)) let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", members: members, canUserTriggerRoomNotification: true))
let service = CompletionSuggestionService(roomProxy: roomProxyMock) let service = CompletionSuggestionService(roomProxy: roomProxyMock)
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:14aa29c8a73cb71b322ba5ba067ebe8f317b477cf4db754c93902061465a1413 oid sha256:b04690d8cebb4b16b56075243f5a337728c51fc7a72c3be0ef6dc60d7d7aede2
size 308497 size 308386
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:14aa29c8a73cb71b322ba5ba067ebe8f317b477cf4db754c93902061465a1413 oid sha256:b04690d8cebb4b16b56075243f5a337728c51fc7a72c3be0ef6dc60d7d7aede2
size 308497 size 308386