Make the room power levels optional as they can be missing depending on what state events are requested

This commit is contained in:
Stefan Ceriu
2025-06-25 12:35:04 +03:00
committed by Stefan Ceriu
parent c7ff6c019d
commit de723853e3
16 changed files with 65 additions and 45 deletions
@@ -8651,13 +8651,13 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable {
return powerLevelsCallsCount > 0 return powerLevelsCallsCount > 0
} }
var powerLevelsUnderlyingReturnValue: Result<RoomPowerLevelsProxyProtocol, RoomProxyError>! var powerLevelsUnderlyingReturnValue: Result<RoomPowerLevelsProxyProtocol?, RoomProxyError>!
var powerLevelsReturnValue: Result<RoomPowerLevelsProxyProtocol, RoomProxyError>! { var powerLevelsReturnValue: Result<RoomPowerLevelsProxyProtocol?, RoomProxyError>! {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return powerLevelsUnderlyingReturnValue return powerLevelsUnderlyingReturnValue
} else { } else {
var returnValue: Result<RoomPowerLevelsProxyProtocol, RoomProxyError>? = nil var returnValue: Result<RoomPowerLevelsProxyProtocol?, RoomProxyError>? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = powerLevelsUnderlyingReturnValue returnValue = powerLevelsUnderlyingReturnValue
} }
@@ -8675,9 +8675,9 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable {
} }
} }
} }
var powerLevelsClosure: (() async -> Result<RoomPowerLevelsProxyProtocol, RoomProxyError>)? var powerLevelsClosure: (() async -> Result<RoomPowerLevelsProxyProtocol?, RoomProxyError>)?
func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol, RoomProxyError> { func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol?, RoomProxyError> {
powerLevelsCallsCount += 1 powerLevelsCallsCount += 1
if let powerLevelsClosure = powerLevelsClosure { if let powerLevelsClosure = powerLevelsClosure {
return await powerLevelsClosure() return await powerLevelsClosure()
@@ -13518,11 +13518,7 @@ class RoomInfoProxyMock: RoomInfoProxyProtocol, @unchecked Sendable {
set(value) { underlyingHistoryVisibility = value } set(value) { underlyingHistoryVisibility = value }
} }
var underlyingHistoryVisibility: RoomHistoryVisibility! var underlyingHistoryVisibility: RoomHistoryVisibility!
var powerLevels: RoomPowerLevelsProxyProtocol { var powerLevels: RoomPowerLevelsProxyProtocol?
get { return underlyingPowerLevels }
set(value) { underlyingPowerLevels = value }
}
var underlyingPowerLevels: RoomPowerLevelsProxyProtocol!
var successor: SuccessorRoom? var successor: SuccessorRoom?
var heroes: [RoomHero] = [] var heroes: [RoomHero] = []
@@ -13587,6 +13583,7 @@ class RoomPowerLevelsProxyMock: RoomPowerLevelsProxyProtocol, @unchecked Sendabl
set(value) { underlyingValues = value } set(value) { underlyingValues = value }
} }
var underlyingValues: RoomPowerLevelsValues! var underlyingValues: RoomPowerLevelsValues!
var userPowerLevels: [String: Int64] = [:]
//MARK: - canOwnUser //MARK: - canOwnUser
@@ -218,9 +218,10 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
state.isKnockableRoom = false state.isKnockableRoom = false
} }
state.canAccept = roomInfo.powerLevels.canOwnUserInvite() guard let powerLevels = roomProxy.infoPublisher.value.powerLevels else { fatalError("Missing room power levels") }
state.canDecline = roomInfo.powerLevels.canOwnUserKick() state.canAccept = powerLevels.canOwnUserInvite()
state.canBan = roomInfo.powerLevels.canOwnUserBan() state.canDecline = powerLevels.canOwnUserKick()
state.canBan = powerLevels.canOwnUserBan()
} }
private static let loadingIndicatorIdentifier = "\(KnockRequestsListScreenViewModel.self)-Loading" private static let loadingIndicatorIdentifier = "\(KnockRequestsListScreenViewModel.self)-Loading"
@@ -87,9 +87,10 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
// MARK: - Private // MARK: - Private
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
state.canEditAvatar = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomAvatar) guard let powerLevels = roomInfo.powerLevels else { fatalError("Missing room power levels") }
state.canEditName = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomName) state.canEditAvatar = powerLevels.canOwnUser(sendStateEvent: .roomAvatar)
state.canEditTopic = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomTopic) state.canEditName = powerLevels.canOwnUser(sendStateEvent: .roomName)
state.canEditTopic = powerLevels.canOwnUser(sendStateEvent: .roomTopic)
} }
private func saveRoomDetails() { private func saveRoomDetails() {
@@ -224,13 +224,17 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
state.isKnockableRoom = false state.isKnockableRoom = false
} }
state.canEditRoomName = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomName) if let powerLevels = roomInfo.powerLevels {
state.canEditRoomTopic = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomTopic) state.canEditRoomName = powerLevels.canOwnUser(sendStateEvent: .roomName)
state.canEditRoomAvatar = roomInfo.powerLevels.canOwnUser(sendStateEvent: .roomAvatar) state.canEditRoomTopic = powerLevels.canOwnUser(sendStateEvent: .roomTopic)
state.canInviteUsers = roomInfo.powerLevels.canOwnUserInvite() state.canEditRoomAvatar = powerLevels.canOwnUser(sendStateEvent: .roomAvatar)
state.canKickUsers = roomInfo.powerLevels.canOwnUserKick() state.canInviteUsers = powerLevels.canOwnUserInvite()
state.canBanUsers = roomInfo.powerLevels.canOwnUserBan() state.canKickUsers = powerLevels.canOwnUserKick()
state.canJoinCall = roomInfo.powerLevels.canOwnUserJoinCall() state.canBanUsers = powerLevels.canOwnUserBan()
state.canJoinCall = powerLevels.canOwnUserJoinCall()
} else {
fatalError("Missing room power levels")
}
Task { Task {
state.canEditRolesOrPermissions = await (try? roomProxy.suggestedRole(for: roomProxy.ownUserID).get()) == .administrator state.canEditRolesOrPermissions = await (try? roomProxy.suggestedRole(for: roomProxy.ownUserID).get()) == .administrator
@@ -88,6 +88,10 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
Task { Task {
showLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier) showLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier)
defer {
hideLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier)
}
let members = members.sorted() let members = members.sorted()
let roomMembersDetails = await buildMembersDetails(members: members) let roomMembersDetails = await buildMembersDetails(members: members)
self.members = members self.members = members
@@ -99,12 +103,10 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
bannedMembers: roomMembersDetails.bannedMembers, bannedMembers: roomMembersDetails.bannedMembers,
bindings: state.bindings) bindings: state.bindings)
let powerLevels = roomProxy.infoPublisher.value.powerLevels guard let powerLevels = roomProxy.infoPublisher.value.powerLevels else { fatalError("Missing room power levels") }
self.state.canInviteUsers = powerLevels.canOwnUserInvite() self.state.canInviteUsers = powerLevels.canOwnUserInvite()
self.state.canKickUsers = powerLevels.canOwnUserKick() self.state.canKickUsers = powerLevels.canOwnUserKick()
self.state.canBanUsers = powerLevels.canOwnUserBan() self.state.canBanUsers = powerLevels.canOwnUserBan()
hideLoadingIndicator(Self.updateStateLoadingIndicatorIdentifier)
} }
} }
@@ -114,7 +114,8 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
// MARK: - Permissions // MARK: - Permissions
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
state.permissions = .init(powerLevels: roomInfo.powerLevels.values) guard let powerLevels = roomInfo.powerLevels else { fatalError("Missing room power levels") }
state.permissions = .init(powerLevels: powerLevels.values)
} }
private func editPermissions(group: RoomRolesAndPermissionsScreenPermissionsGroup) { private func editPermissions(group: RoomRolesAndPermissionsScreenPermissionsGroup) {
@@ -47,7 +47,8 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
self?.suggestionTriggerSubject.value != nil ? .milliseconds(500) : .milliseconds(0) self?.suggestionTriggerSubject.value != nil ? .milliseconds(500) : .milliseconds(0)
} }
canMentionAllUsers = roomProxy.infoPublisher.value.powerLevels.canOwnUserTriggerRoomNotification() guard let powerLevels = roomProxy.infoPublisher.value.powerLevels else { fatalError("Missing room power levels") }
canMentionAllUsers = powerLevels.canOwnUserTriggerRoomNotification()
} }
func processTextMessage(_ textMessage: String, selectedRange: NSRange) { func processTextMessage(_ textMessage: String, selectedRange: NSRange) {
@@ -348,11 +348,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.isKnockableRoom = false state.isKnockableRoom = false
} }
state.canSendMessage = roomInfo.powerLevels.canOwnUser(sendMessage: .roomMessage) guard let powerLevels = roomInfo.powerLevels else { fatalError("Missing room power levels") }
state.canJoinCall = roomInfo.powerLevels.canOwnUserJoinCall() state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
state.canAcceptKnocks = roomInfo.powerLevels.canOwnUserInvite() state.canJoinCall = powerLevels.canOwnUserJoinCall()
state.canDeclineKnocks = roomInfo.powerLevels.canOwnUserKick() state.canAcceptKnocks = powerLevels.canOwnUserInvite()
state.canBan = roomInfo.powerLevels.canOwnUserBan() state.canDeclineKnocks = powerLevels.canOwnUserKick()
state.canBan = powerLevels.canOwnUserBan()
} }
private func setupPinnedEventsTimelineItemProviderIfNeeded() { private func setupPinnedEventsTimelineItemProviderIfNeeded() {
@@ -63,6 +63,7 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
// MARK: - Private // MARK: - Private
private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxyProtocol) { private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxyProtocol) {
state.canSendMessage = roomInfo.powerLevels.canOwnUser(sendMessage: .roomMessage) guard let powerLevels = roomInfo.powerLevels else { fatalError("Missing room power levels") }
state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
} }
} }
@@ -405,12 +405,13 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(roomInfo: RoomInfoProxyProtocol) {
state.pinnedEventIDs = roomInfo.pinnedEventIDs state.pinnedEventIDs = roomInfo.pinnedEventIDs
state.canCurrentUserSendMessage = roomInfo.powerLevels.canOwnUser(sendMessage: .roomMessage) guard let powerLevels = roomInfo.powerLevels else { fatalError("Missing room power levels") }
state.canCurrentUserRedactOthers = roomInfo.powerLevels.canOwnUserRedactOther() state.canCurrentUserSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
state.canCurrentUserRedactSelf = roomInfo.powerLevels.canOwnUserRedactOwn() state.canCurrentUserRedactOthers = powerLevels.canOwnUserRedactOther()
state.canCurrentUserPin = roomInfo.powerLevels.canOwnUserPinOrUnpin() state.canCurrentUserRedactSelf = powerLevels.canOwnUserRedactOwn()
state.canCurrentUserKick = roomInfo.powerLevels.canOwnUserKick() state.canCurrentUserPin = powerLevels.canOwnUserPinOrUnpin()
state.canCurrentUserBan = roomInfo.powerLevels.canOwnUserBan() state.canCurrentUserKick = powerLevels.canOwnUserKick()
state.canCurrentUserBan = powerLevels.canOwnUserBan()
} }
private func setupSubscriptions() { private func setupSubscriptions() {
@@ -552,7 +552,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
// MARK: - Power Levels // MARK: - Power Levels
func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol, RoomProxyError> { func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol?, RoomProxyError> {
do { do {
return try await .success(RoomPowerLevelsProxy(room.getPowerLevels())) return try await .success(RoomPowerLevelsProxy(room.getPowerLevels()))
} catch { } catch {
@@ -53,7 +53,7 @@ struct RoomInfoProxy: RoomInfoProxyProtocol {
var joinRule: JoinRule? { roomInfo.joinRule } var joinRule: JoinRule? { roomInfo.joinRule }
var historyVisibility: RoomHistoryVisibility { roomInfo.historyVisibility } var historyVisibility: RoomHistoryVisibility { roomInfo.historyVisibility }
var powerLevels: RoomPowerLevelsProxyProtocol { RoomPowerLevelsProxy(roomInfo.powerLevels) } var powerLevels: RoomPowerLevelsProxyProtocol? { RoomPowerLevelsProxy(roomInfo.powerLevels) }
} }
struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol { struct RoomPreviewInfoProxy: BaseRoomInfoProxyProtocol {
@@ -63,7 +63,7 @@ protocol RoomInfoProxyProtocol: BaseRoomInfoProxyProtocol {
var joinRule: JoinRule? { get } var joinRule: JoinRule? { get }
var historyVisibility: RoomHistoryVisibility { get } var historyVisibility: RoomHistoryVisibility { get }
var powerLevels: RoomPowerLevelsProxyProtocol { get } var powerLevels: RoomPowerLevelsProxyProtocol? { get }
} }
extension BaseRoomInfoProxyProtocol { extension BaseRoomInfoProxyProtocol {
@@ -11,6 +11,8 @@ import MatrixRustSDK
protocol RoomPowerLevelsProxyProtocol { protocol RoomPowerLevelsProxyProtocol {
var values: RoomPowerLevelsValues { get } var values: RoomPowerLevelsValues { get }
var userPowerLevels: [String: Int64] { get }
func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool
func canOwnUser(sendStateEvent event: StateEventType) -> Bool func canOwnUser(sendStateEvent event: StateEventType) -> Bool
func canOwnUserInvite() -> Bool func canOwnUserInvite() -> Bool
@@ -10,7 +10,11 @@ import MatrixRustSDK
struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol { struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol {
private let powerLevels: RoomPowerLevels private let powerLevels: RoomPowerLevels
init(_ powerLevels: RoomPowerLevels) { init?(_ powerLevels: RoomPowerLevels?) {
guard let powerLevels else {
return nil
}
self.powerLevels = powerLevels self.powerLevels = powerLevels
} }
@@ -18,6 +22,10 @@ struct RoomPowerLevelsProxy: RoomPowerLevelsProxyProtocol {
powerLevels.values() powerLevels.values()
} }
var userPowerLevels: [String: Int64] {
powerLevels.userPowerLevels()
}
func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool { func canOwnUser(sendMessage messageType: MessageLikeEventType) -> Bool {
powerLevels.canOwnUserSendMessage(message: messageType) powerLevels.canOwnUserSendMessage(message: messageType)
} }
@@ -152,7 +152,7 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
// MARK: - Power Levels // MARK: - Power Levels
func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol, RoomProxyError> func powerLevels() async -> Result<RoomPowerLevelsProxyProtocol?, RoomProxyError>
func applyPowerLevelChanges(_ changes: RoomPowerLevelChanges) async -> Result<Void, RoomProxyError> func applyPowerLevelChanges(_ changes: RoomPowerLevelChanges) async -> Result<Void, RoomProxyError>
func resetPowerLevels() async -> Result<Void, RoomProxyError> func resetPowerLevels() async -> Result<Void, RoomProxyError>
func suggestedRole(for userID: String) async -> Result<RoomMemberRole, RoomProxyError> func suggestedRole(for userID: String) async -> Result<RoomMemberRole, RoomProxyError>