Update the SDK and handle breaking changes. (#2527)

This commit is contained in:
Doug
2024-03-05 14:08:34 +00:00
committed by GitHub
parent 4320e5c9e0
commit 1b5db6c63d
39 changed files with 455 additions and 504 deletions
+1 -1
View File
@@ -7044,7 +7044,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = { requirement = {
kind = exactVersion; kind = exactVersion;
version = 1.1.44; version = 1.1.45;
}; };
}; };
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
@@ -130,8 +130,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift", "location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : { "state" : {
"revision" : "c8d0dd3b7756584276ffb95ffc261aada654e0fa", "revision" : "55425c708ba6377ef319f85f6f70c7caf2187368",
"version" : "1.1.44" "version" : "1.1.45"
} }
}, },
{ {
@@ -256,8 +256,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case (.roomDetails, .dismissRoom, .initial): case (.roomDetails, .dismissRoom, .initial):
dismissRoom(animated: animated) dismissRoom(animated: animated)
case (.roomDetails, .presentRoomDetailsEditScreen(let accountOwner), .roomDetailsEditScreen): case (.roomDetails, .presentRoomDetailsEditScreen, .roomDetailsEditScreen):
presentRoomDetailsEditScreen(accountOwner: accountOwner.value) presentRoomDetailsEditScreen()
case (.roomDetailsEditScreen, .dismissRoomDetailsEditScreen, .roomDetails): case (.roomDetailsEditScreen, .dismissRoomDetailsEditScreen, .roomDetails):
break break
@@ -520,8 +520,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
return return
} }
let params = RoomDetailsScreenCoordinatorParameters(accountUserID: userSession.userID, let params = RoomDetailsScreenCoordinatorParameters(roomProxy: roomProxy,
roomProxy: roomProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
analyticsService: analytics, analyticsService: analytics,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
@@ -537,8 +537,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
dismissRoom(animated: animated) dismissRoom(animated: animated)
case .presentRoomMembersList: case .presentRoomMembersList:
stateMachine.tryEvent(.presentRoomMembersList) stateMachine.tryEvent(.presentRoomMembersList)
case .presentRoomDetailsEditScreen(let accountOwner): case .presentRoomDetailsEditScreen:
stateMachine.tryEvent(.presentRoomDetailsEditScreen(accountOwner: .init(value: accountOwner))) stateMachine.tryEvent(.presentRoomDetailsEditScreen)
case .presentNotificationSettingsScreen: case .presentNotificationSettingsScreen:
stateMachine.tryEvent(.presentNotificationSettingsScreen) stateMachine.tryEvent(.presentNotificationSettingsScreen)
case .presentInviteUsersScreen: case .presentInviteUsersScreen:
@@ -600,17 +600,16 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
private func presentRoomDetailsEditScreen(accountOwner: RoomMemberProxyProtocol) { private func presentRoomDetailsEditScreen() {
guard let roomProxy else { guard let roomProxy else {
fatalError() fatalError()
} }
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(accountOwner: accountOwner, let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
navigationStackCoordinator: stackCoordinator, navigationStackCoordinator: stackCoordinator,
roomProxy: roomProxy,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
orientationManager: orientationManager) orientationManager: orientationManager)
let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters) let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters)
@@ -639,6 +638,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = ReportContentScreenCoordinatorParameters(eventID: eventID, let parameters = ReportContentScreenCoordinatorParameters(eventID: eventID,
senderID: senderID, senderID: senderID,
roomProxy: roomProxy, roomProxy: roomProxy,
clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
let coordinator = ReportContentScreenCoordinator(parameters: parameters) let coordinator = ReportContentScreenCoordinator(parameters: parameters)
@@ -928,8 +928,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
fatalError() fatalError()
} }
let params = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: roomProxy, let params = RoomMemberDetailsScreenCoordinatorParameters(userID: userID,
userID: userID, roomProxy: roomProxy,
clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params) let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params)
@@ -1205,7 +1206,7 @@ private extension RoomFlowCoordinator {
case presentRoomDetails(roomID: String) case presentRoomDetails(roomID: String)
case dismissRoomDetails case dismissRoomDetails
case presentRoomDetailsEditScreen(accountOwner: HashableRoomMemberWrapper) case presentRoomDetailsEditScreen
case dismissRoomDetailsEditScreen case dismissRoomDetailsEditScreen
case presentNotificationSettingsScreen case presentNotificationSettingsScreen
+3 -3
View File
@@ -18,7 +18,7 @@ import Combine
import Foundation import Foundation
struct ClientProxyMockConfiguration { struct ClientProxyMockConfiguration {
let userID: String var userID: String = RoomMemberProxyMock.mockMe.userID
var deviceID: String? var deviceID: String?
var roomSummaryProvider: RoomSummaryProviderProtocol? = RoomSummaryProviderMock(.init()) var roomSummaryProvider: RoomSummaryProviderProtocol? = RoomSummaryProviderMock(.init())
} }
@@ -62,8 +62,8 @@ extension ClientProxyMock {
searchUsersSearchTermLimitReturnValue = .success(.init(results: [], limited: false)) searchUsersSearchTermLimitReturnValue = .success(.init(results: [], limited: false))
profileForReturnValue = .success(.init(userID: "@a:b.com", displayName: "Some user")) profileForReturnValue = .success(.init(userID: "@a:b.com", displayName: "Some user"))
sessionVerificationControllerProxyReturnValue = .failure(.failedRetrievingSessionVerificationController) sessionVerificationControllerProxyReturnValue = .failure(.failedRetrievingSessionVerificationController)
ignoreUserReturnValue = .failure(.failedIgnoringUser) ignoreUserReturnValue = .success(())
unignoreUserReturnValue = .failure(.failedUnignoringUser) unignoreUserReturnValue = .success(())
loadMediaContentForSourceThrowableError = ClientProxyError.failedLoadingMedia loadMediaContentForSourceThrowableError = ClientProxyError.failedLoadingMedia
loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.failedLoadingMedia loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.failedLoadingMedia
@@ -2253,11 +2253,6 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol {
set(value) { underlyingMembership = value } set(value) { underlyingMembership = value }
} }
var underlyingMembership: MembershipState! var underlyingMembership: MembershipState!
var isAccountOwner: Bool {
get { return underlyingIsAccountOwner }
set(value) { underlyingIsAccountOwner = value }
}
var underlyingIsAccountOwner: Bool!
var isIgnored: Bool { var isIgnored: Bool {
get { return underlyingIsIgnored } get { return underlyingIsIgnored }
set(value) { underlyingIsIgnored = value } set(value) { underlyingIsIgnored = value }
@@ -2273,77 +2268,7 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol {
set(value) { underlyingRole = value } set(value) { underlyingRole = value }
} }
var underlyingRole: RoomMemberRole! var underlyingRole: RoomMemberRole!
var canInviteUsers: Bool {
get { return underlyingCanInviteUsers }
set(value) { underlyingCanInviteUsers = value }
}
var underlyingCanInviteUsers: Bool!
var canKickUsers: Bool {
get { return underlyingCanKickUsers }
set(value) { underlyingCanKickUsers = value }
}
var underlyingCanKickUsers: Bool!
var canBanUsers: Bool {
get { return underlyingCanBanUsers }
set(value) { underlyingCanBanUsers = value }
}
var underlyingCanBanUsers: Bool!
//MARK: - ignoreUser
var ignoreUserCallsCount = 0
var ignoreUserCalled: Bool {
return ignoreUserCallsCount > 0
}
var ignoreUserReturnValue: Result<Void, RoomMemberProxyError>!
var ignoreUserClosure: (() async -> Result<Void, RoomMemberProxyError>)?
func ignoreUser() async -> Result<Void, RoomMemberProxyError> {
ignoreUserCallsCount += 1
if let ignoreUserClosure = ignoreUserClosure {
return await ignoreUserClosure()
} else {
return ignoreUserReturnValue
}
}
//MARK: - unignoreUser
var unignoreUserCallsCount = 0
var unignoreUserCalled: Bool {
return unignoreUserCallsCount > 0
}
var unignoreUserReturnValue: Result<Void, RoomMemberProxyError>!
var unignoreUserClosure: (() async -> Result<Void, RoomMemberProxyError>)?
func unignoreUser() async -> Result<Void, RoomMemberProxyError> {
unignoreUserCallsCount += 1
if let unignoreUserClosure = unignoreUserClosure {
return await unignoreUserClosure()
} else {
return unignoreUserReturnValue
}
}
//MARK: - canSendStateEvent
var canSendStateEventTypeCallsCount = 0
var canSendStateEventTypeCalled: Bool {
return canSendStateEventTypeCallsCount > 0
}
var canSendStateEventTypeReceivedType: StateEventType?
var canSendStateEventTypeReceivedInvocations: [StateEventType] = []
var canSendStateEventTypeReturnValue: Bool!
var canSendStateEventTypeClosure: ((StateEventType) -> Bool)?
func canSendStateEvent(type: StateEventType) -> Bool {
canSendStateEventTypeCallsCount += 1
canSendStateEventTypeReceivedType = type
canSendStateEventTypeReceivedInvocations.append(type)
if let canSendStateEventTypeClosure = canSendStateEventTypeClosure {
return canSendStateEventTypeClosure(type)
} else {
return canSendStateEventTypeReturnValue
}
}
} }
class RoomNotificationSettingsProxyMock: RoomNotificationSettingsProxyProtocol { class RoomNotificationSettingsProxyMock: RoomNotificationSettingsProxyProtocol {
var mode: RoomNotificationModeProxy { var mode: RoomNotificationModeProxy {
@@ -2517,27 +2442,6 @@ class RoomProxyMock: RoomProxyProtocol {
return reportContentReasonReturnValue return reportContentReasonReturnValue
} }
} }
//MARK: - ignoreUser
var ignoreUserCallsCount = 0
var ignoreUserCalled: Bool {
return ignoreUserCallsCount > 0
}
var ignoreUserReceivedUserID: String?
var ignoreUserReceivedInvocations: [String] = []
var ignoreUserReturnValue: Result<Void, RoomProxyError>!
var ignoreUserClosure: ((String) async -> Result<Void, RoomProxyError>)?
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError> {
ignoreUserCallsCount += 1
ignoreUserReceivedUserID = userID
ignoreUserReceivedInvocations.append(userID)
if let ignoreUserClosure = ignoreUserClosure {
return await ignoreUserClosure(userID)
} else {
return ignoreUserReturnValue
}
}
//MARK: - leaveRoom //MARK: - leaveRoom
var leaveRoomCallsCount = 0 var leaveRoomCallsCount = 0
@@ -2846,6 +2750,48 @@ class RoomProxyMock: RoomProxyProtocol {
return applyPowerLevelChangesReturnValue return applyPowerLevelChangesReturnValue
} }
} }
//MARK: - canUser
var canUserUserIDSendStateEventCallsCount = 0
var canUserUserIDSendStateEventCalled: Bool {
return canUserUserIDSendStateEventCallsCount > 0
}
var canUserUserIDSendStateEventReceivedArguments: (userID: String, event: StateEventType)?
var canUserUserIDSendStateEventReceivedInvocations: [(userID: String, event: StateEventType)] = []
var canUserUserIDSendStateEventReturnValue: Result<Bool, RoomProxyError>!
var canUserUserIDSendStateEventClosure: ((String, StateEventType) async -> Result<Bool, RoomProxyError>)?
func canUser(userID: String, sendStateEvent event: StateEventType) async -> Result<Bool, RoomProxyError> {
canUserUserIDSendStateEventCallsCount += 1
canUserUserIDSendStateEventReceivedArguments = (userID: userID, event: event)
canUserUserIDSendStateEventReceivedInvocations.append((userID: userID, event: event))
if let canUserUserIDSendStateEventClosure = canUserUserIDSendStateEventClosure {
return await canUserUserIDSendStateEventClosure(userID, event)
} else {
return canUserUserIDSendStateEventReturnValue
}
}
//MARK: - canUserInvite
var canUserInviteUserIDCallsCount = 0
var canUserInviteUserIDCalled: Bool {
return canUserInviteUserIDCallsCount > 0
}
var canUserInviteUserIDReceivedUserID: String?
var canUserInviteUserIDReceivedInvocations: [String] = []
var canUserInviteUserIDReturnValue: Result<Bool, RoomProxyError>!
var canUserInviteUserIDClosure: ((String) async -> Result<Bool, RoomProxyError>)?
func canUserInvite(userID: String) async -> Result<Bool, RoomProxyError> {
canUserInviteUserIDCallsCount += 1
canUserInviteUserIDReceivedUserID = userID
canUserInviteUserIDReceivedInvocations.append(userID)
if let canUserInviteUserIDClosure = canUserInviteUserIDClosure {
return await canUserInviteUserIDClosure(userID)
} else {
return canUserInviteUserIDReturnValue
}
}
//MARK: - canUserRedactOther //MARK: - canUserRedactOther
var canUserRedactOtherUserIDCallsCount = 0 var canUserRedactOtherUserIDCallsCount = 0
@@ -2888,6 +2834,48 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserRedactOwnUserIDReturnValue return canUserRedactOwnUserIDReturnValue
} }
} }
//MARK: - canUserKick
var canUserKickUserIDCallsCount = 0
var canUserKickUserIDCalled: Bool {
return canUserKickUserIDCallsCount > 0
}
var canUserKickUserIDReceivedUserID: String?
var canUserKickUserIDReceivedInvocations: [String] = []
var canUserKickUserIDReturnValue: Result<Bool, RoomProxyError>!
var canUserKickUserIDClosure: ((String) async -> Result<Bool, RoomProxyError>)?
func canUserKick(userID: String) async -> Result<Bool, RoomProxyError> {
canUserKickUserIDCallsCount += 1
canUserKickUserIDReceivedUserID = userID
canUserKickUserIDReceivedInvocations.append(userID)
if let canUserKickUserIDClosure = canUserKickUserIDClosure {
return await canUserKickUserIDClosure(userID)
} else {
return canUserKickUserIDReturnValue
}
}
//MARK: - canUserBan
var canUserBanUserIDCallsCount = 0
var canUserBanUserIDCalled: Bool {
return canUserBanUserIDCallsCount > 0
}
var canUserBanUserIDReceivedUserID: String?
var canUserBanUserIDReceivedInvocations: [String] = []
var canUserBanUserIDReturnValue: Result<Bool, RoomProxyError>!
var canUserBanUserIDClosure: ((String) async -> Result<Bool, RoomProxyError>)?
func canUserBan(userID: String) async -> Result<Bool, RoomProxyError> {
canUserBanUserIDCallsCount += 1
canUserBanUserIDReceivedUserID = userID
canUserBanUserIDReceivedInvocations.append(userID)
if let canUserBanUserIDClosure = canUserBanUserIDClosure {
return await canUserBanUserIDClosure(userID)
} else {
return canUserBanUserIDReturnValue
}
}
//MARK: - canUserTriggerRoomNotification //MARK: - canUserTriggerRoomNotification
var canUserTriggerRoomNotificationUserIDCallsCount = 0 var canUserTriggerRoomNotificationUserIDCallsCount = 0
@@ -21,15 +21,12 @@ struct RoomMemberProxyMockConfiguration {
var userID: String var userID: String
var displayName: String? var displayName: String?
var avatarURL: URL? var avatarURL: URL?
var membership: MembershipState var membership: MembershipState
var isAccountOwner = false
var isIgnored = false var isIgnored = false
var powerLevel = 0 var powerLevel = 0
var role = RoomMemberRole.user var role = RoomMemberRole.user
var canInviteUsers = false
var canKickUsers = false
var canBanUsers = false
var canSendStateEvent: (StateEventType) -> Bool = { _ in true }
} }
extension RoomMemberProxyMock { extension RoomMemberProxyMock {
@@ -38,15 +35,12 @@ extension RoomMemberProxyMock {
userID = configuration.userID userID = configuration.userID
displayName = configuration.displayName displayName = configuration.displayName
avatarURL = configuration.avatarURL avatarURL = configuration.avatarURL
membership = configuration.membership membership = configuration.membership
isAccountOwner = configuration.isAccountOwner
isIgnored = configuration.isIgnored isIgnored = configuration.isIgnored
powerLevel = configuration.powerLevel powerLevel = configuration.powerLevel
role = configuration.role role = configuration.role
canInviteUsers = configuration.canInviteUsers
canKickUsers = configuration.canKickUsers
canBanUsers = configuration.canBanUsers
canSendStateEventTypeClosure = configuration.canSendStateEvent
} }
// Mocks // Mocks
@@ -54,9 +48,7 @@ extension RoomMemberProxyMock {
RoomMemberProxyMock(with: .init(userID: "@me:matrix.org", RoomMemberProxyMock(with: .init(userID: "@me:matrix.org",
displayName: "Me", displayName: "Me",
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
membership: .join, membership: .join))
isAccountOwner: true,
canInviteUsers: true))
} }
static var mockMeAdmin: RoomMemberProxyMock { static var mockMeAdmin: RoomMemberProxyMock {
@@ -64,12 +56,8 @@ extension RoomMemberProxyMock {
displayName: "Me admin", displayName: "Me admin",
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
membership: .join, membership: .join,
isAccountOwner: true,
powerLevel: 100, powerLevel: 100,
role: .administrator, role: .administrator))
canInviteUsers: true,
canKickUsers: true,
canBanUsers: true))
} }
static var mockAlice: RoomMemberProxyMock { static var mockAlice: RoomMemberProxyMock {
@@ -117,15 +105,6 @@ extension RoomMemberProxyMock {
isIgnored: true)) isIgnored: true))
} }
static func mockOwner(allowedStateEvents: [StateEventType], canInviteUsers: Bool = true) -> RoomMemberProxyMock {
RoomMemberProxyMock(with: .init(userID: "@foo:some.org",
displayName: "User owner",
membership: .join,
isAccountOwner: true,
canInviteUsers: canInviteUsers,
canSendStateEvent: { allowedStateEvents.contains($0) }))
}
static var mockAdmin: RoomMemberProxyMock { static var mockAdmin: RoomMemberProxyMock {
RoomMemberProxyMock(with: .init(userID: "@admin:matrix.org", RoomMemberProxyMock(with: .init(userID: "@admin:matrix.org",
displayName: "Arthur", displayName: "Arthur",
+12 -1
View File
@@ -43,8 +43,9 @@ struct RoomProxyMockConfiguration {
var members: [RoomMemberProxyMock] = .allMembers var members: [RoomMemberProxyMock] = .allMembers
var memberForID: RoomMemberProxyMock = .mockMe var memberForID: RoomMemberProxyMock = .mockMe
var ownUserID = "@alice:somewhere.org" var ownUserID = RoomMemberProxyMock.mockMe.userID
var canUserInvite = true
var canUserTriggerRoomNotification = false var canUserTriggerRoomNotification = false
var canUserJoinCall = true var canUserJoinCall = true
} }
@@ -89,7 +90,17 @@ extension RoomProxyMock {
currentPowerLevelChangesReturnValue = .success(.init()) currentPowerLevelChangesReturnValue = .success(.init())
applyPowerLevelChangesReturnValue = .success(()) applyPowerLevelChangesReturnValue = .success(())
canUserUserIDSendStateEventClosure = { [weak self] userID, _ in
.success(self?.membersPublisher.value.first { $0.userID == userID }?.role ?? .user != .user)
}
canUserInviteUserIDReturnValue = .success(configuration.canUserInvite)
canUserRedactOtherUserIDReturnValue = .success(false) canUserRedactOtherUserIDReturnValue = .success(false)
canUserKickUserIDClosure = { [weak self] userID in
.success(self?.membersPublisher.value.first { $0.userID == userID }?.role ?? .user != .user)
}
canUserBanUserIDClosure = { [weak self] userID in
.success(self?.membersPublisher.value.first { $0.userID == userID }?.role ?? .user != .user)
}
canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification) canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification)
canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall) canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall)
@@ -28,7 +28,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
self.roomProxy = roomProxy self.roomProxy = roomProxy
suggestionsPublisher = suggestionTriggerSubject suggestionsPublisher = suggestionTriggerSubject
.combineLatest(roomProxy.membersPublisher) .combineLatest(roomProxy.membersPublisher)
.map { [weak self] suggestionPattern, members -> [SuggestionItem] in .map { [weak self, ownUserID = roomProxy.ownUserID] suggestionPattern, members -> [SuggestionItem] in
guard let self, guard let self,
let suggestionPattern else { let suggestionPattern else {
return [] return []
@@ -38,7 +38,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
case .user: case .user:
var membersSuggestion = members var membersSuggestion = members
.compactMap { member -> SuggestionItem? in .compactMap { member -> SuggestionItem? in
guard !member.isAccountOwner, guard member.userID != ownUserID,
member.membership == .join, member.membership == .join,
Self.isIncluded(searchText: suggestionPattern.text, userID: member.userID, displayName: member.displayName) else { Self.isIncluded(searchText: suggestionPattern.text, userID: member.userID, displayName: member.displayName) else {
return nil return nil
@@ -21,6 +21,7 @@ struct ReportContentScreenCoordinatorParameters {
let eventID: String let eventID: String
let senderID: String let senderID: String
let roomProxy: RoomProxyProtocol let roomProxy: RoomProxyProtocol
let clientProxy: ClientProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -42,7 +43,10 @@ final class ReportContentScreenCoordinator: CoordinatorProtocol {
init(parameters: ReportContentScreenCoordinatorParameters) { init(parameters: ReportContentScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = ReportContentScreenViewModel(eventID: parameters.eventID, senderID: parameters.senderID, roomProxy: parameters.roomProxy) viewModel = ReportContentScreenViewModel(eventID: parameters.eventID,
senderID: parameters.senderID,
roomProxy: parameters.roomProxy,
clientProxy: parameters.clientProxy)
} }
// MARK: - Public // MARK: - Public
@@ -55,9 +59,9 @@ final class ReportContentScreenCoordinator: CoordinatorProtocol {
switch action { switch action {
case .submitStarted: case .submitStarted:
startLoading() startLoading()
case let .submitFailed(error): case let .submitFailed(message):
stopLoading() stopLoading()
showError(description: error.localizedDescription) showError(description: message)
case .submitFinished: case .submitFinished:
stopLoading() stopLoading()
actionsSubject.send(.finish) actionsSubject.send(.finish)
@@ -20,7 +20,7 @@ enum ReportContentScreenViewModelAction: Equatable {
case cancel case cancel
case submitStarted case submitStarted
case submitFinished case submitFinished
case submitFailed(error: RoomProxyError) case submitFailed(message: String)
} }
struct ReportContentScreenViewState: BindableState { struct ReportContentScreenViewState: BindableState {
@@ -23,16 +23,18 @@ class ReportContentScreenViewModel: ReportContentScreenViewModelType, ReportCont
private let eventID: String private let eventID: String
private let senderID: String private let senderID: String
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol
private let clientProxy: ClientProxyProtocol
private let actionsSubject: PassthroughSubject<ReportContentScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<ReportContentScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<ReportContentScreenViewModelAction, Never> { var actions: AnyPublisher<ReportContentScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(eventID: String, senderID: String, roomProxy: RoomProxyProtocol) { init(eventID: String, senderID: String, roomProxy: RoomProxyProtocol, clientProxy: ClientProxyProtocol) {
self.eventID = eventID self.eventID = eventID
self.senderID = senderID self.senderID = senderID
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.clientProxy = clientProxy
super.init(initialViewState: ReportContentScreenViewState(bindings: ReportContentScreenViewStateBindings(reasonText: "", ignoreUser: false))) super.init(initialViewState: ReportContentScreenViewState(bindings: ReportContentScreenViewStateBindings(reasonText: "", ignoreUser: false)))
} }
@@ -55,14 +57,14 @@ class ReportContentScreenViewModel: ReportContentScreenViewModelType, ReportCont
if case let .failure(error) = await roomProxy.reportContent(eventID, reason: state.bindings.reasonText) { if case let .failure(error) = await roomProxy.reportContent(eventID, reason: state.bindings.reasonText) {
MXLog.error("Submit Report Content failed: \(error)") MXLog.error("Submit Report Content failed: \(error)")
actionsSubject.send(.submitFailed(error: error)) actionsSubject.send(.submitFailed(message: error.localizedDescription))
return return
} }
// Ignore the sender if the user wants to. // Ignore the sender if the user wants to.
if state.bindings.ignoreUser, case let .failure(error) = await roomProxy.ignoreUser(senderID) { if state.bindings.ignoreUser, case let .failure(error) = await clientProxy.ignoreUser(senderID) {
MXLog.error("Ignore user failed: \(error)") MXLog.error("Ignore user failed: \(error)")
actionsSubject.send(.submitFailed(error: error)) actionsSubject.send(.submitFailed(message: error.localizedDescription))
return return
} }
@@ -77,7 +77,8 @@ struct ReportContentScreen: View {
struct ReportContentScreen_Previews: PreviewProvider, TestablePreview { struct ReportContentScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = ReportContentScreenViewModel(eventID: "", static let viewModel = ReportContentScreenViewModel(eventID: "",
senderID: "", senderID: "",
roomProxy: RoomProxyMock(with: .init())) roomProxy: RoomProxyMock(with: .init()),
clientProxy: ClientProxyMock(.init()))
static var previews: some View { static var previews: some View {
NavigationStack { NavigationStack {
@@ -18,10 +18,9 @@ import Combine
import SwiftUI import SwiftUI
struct RoomDetailsEditScreenCoordinatorParameters { struct RoomDetailsEditScreenCoordinatorParameters {
let accountOwner: RoomMemberProxyProtocol let roomProxy: RoomProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
weak var navigationStackCoordinator: NavigationStackCoordinator? weak var navigationStackCoordinator: NavigationStackCoordinator?
let roomProxy: RoomProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let orientationManager: OrientationManagerProtocol let orientationManager: OrientationManagerProtocol
} }
@@ -43,9 +42,8 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol {
init(parameters: RoomDetailsEditScreenCoordinatorParameters) { init(parameters: RoomDetailsEditScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = RoomDetailsEditScreenViewModel(accountOwner: parameters.accountOwner, viewModel = RoomDetailsEditScreenViewModel(roomProxy: parameters.roomProxy,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
roomProxy: parameters.roomProxy,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
} }
@@ -34,9 +34,9 @@ struct RoomDetailsEditScreenViewState: BindableState {
let initialAvatarURL: URL? let initialAvatarURL: URL?
let initialName: String let initialName: String
let initialTopic: String let initialTopic: String
let canEditAvatar: Bool var canEditAvatar = false
let canEditName: Bool var canEditName = false
let canEditTopic: Bool var canEditTopic = false
var avatarURL: URL? var avatarURL: URL?
var localMedia: MediaInfo? var localMedia: MediaInfo?
@@ -29,9 +29,8 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(accountOwner: RoomMemberProxyProtocol, init(roomProxy: RoomProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
roomProxy: RoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -44,11 +43,15 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
initialAvatarURL: roomAvatar, initialAvatarURL: roomAvatar,
initialName: roomName ?? "", initialName: roomName ?? "",
initialTopic: roomTopic ?? "", initialTopic: roomTopic ?? "",
canEditAvatar: accountOwner.canSendStateEvent(type: .roomAvatar),
canEditName: accountOwner.canSendStateEvent(type: .roomName),
canEditTopic: accountOwner.canSendStateEvent(type: .roomTopic),
avatarURL: roomAvatar, avatarURL: roomAvatar,
bindings: .init(name: roomName ?? "", topic: roomTopic ?? "")), imageProvider: mediaProvider) bindings: .init(name: roomName ?? "", topic: roomTopic ?? "")), imageProvider: mediaProvider)
Task {
// Can't use async let because the mocks aren't thread safe when calling the same method 🤦‍♂️
state.canEditAvatar = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar) == .success(true)
state.canEditName = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName) == .success(true)
state.canEditTopic = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic) == .success(true)
}
} }
// MARK: - Public // MARK: - Public
@@ -156,16 +156,18 @@ struct RoomDetailsEditScreen: View {
// MARK: - Previews // MARK: - Previews
struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview { struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = RoomDetailsEditScreenViewModel(accountOwner: RoomMemberProxyMock.mockAlice, static let viewModel = RoomDetailsEditScreenViewModel(roomProxy: RoomProxyMock(with: .init(id: "test_id",
name: "Room",
members: [.mockMeAdmin])),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
roomProxy: RoomProxyMock(with: .init(id: "test_id", name: "Room")),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
static let readOnlyViewModel = { static let readOnlyViewModel = {
let accountOwner = RoomMemberProxyMock.mockOwner(allowedStateEvents: []) let accountOwner = RoomMemberProxyMock.mockMe
return RoomDetailsEditScreenViewModel(accountOwner: accountOwner, return RoomDetailsEditScreenViewModel(roomProxy: RoomProxyMock(with: .init(id: "test_id",
name: "Room",
members: [.mockMe])),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
roomProxy: RoomProxyMock(with: .init(id: "test_id", name: "Room")),
userIndicatorController: UserIndicatorControllerMock.default) userIndicatorController: UserIndicatorControllerMock.default)
}() }()
@@ -174,10 +176,12 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
RoomDetailsEditScreen(context: viewModel.context) RoomDetailsEditScreen(context: viewModel.context)
} }
.previewDisplayName("Normal") .previewDisplayName("Normal")
.snapshot(delay: 0.1)
NavigationStack { NavigationStack {
RoomDetailsEditScreen(context: readOnlyViewModel.context) RoomDetailsEditScreen(context: readOnlyViewModel.context)
} }
.previewDisplayName("Read only") .previewDisplayName("Read only")
.snapshot(delay: 0.1)
} }
} }
@@ -18,8 +18,8 @@ import Combine
import SwiftUI import SwiftUI
struct RoomDetailsScreenCoordinatorParameters { struct RoomDetailsScreenCoordinatorParameters {
let accountUserID: String
let roomProxy: RoomProxyProtocol let roomProxy: RoomProxyProtocol
let clientProxy: ClientProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let analyticsService: AnalyticsService let analyticsService: AnalyticsService
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -30,7 +30,7 @@ struct RoomDetailsScreenCoordinatorParameters {
enum RoomDetailsScreenCoordinatorAction { enum RoomDetailsScreenCoordinatorAction {
case leftRoom case leftRoom
case presentRoomMembersList case presentRoomMembersList
case presentRoomDetailsEditScreen(accountOwner: RoomMemberProxyProtocol) case presentRoomDetailsEditScreen
case presentNotificationSettingsScreen case presentNotificationSettingsScreen
case presentInviteUsersScreen case presentInviteUsersScreen
case presentPollsHistory case presentPollsHistory
@@ -47,8 +47,8 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
} }
init(parameters: RoomDetailsScreenCoordinatorParameters) { init(parameters: RoomDetailsScreenCoordinatorParameters) {
viewModel = RoomDetailsScreenViewModel(accountUserID: parameters.accountUserID, viewModel = RoomDetailsScreenViewModel(roomProxy: parameters.roomProxy,
roomProxy: parameters.roomProxy, clientProxy: parameters.clientProxy,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
analyticsService: parameters.analyticsService, analyticsService: parameters.analyticsService,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
@@ -70,8 +70,8 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.presentInviteUsersScreen) actionsSubject.send(.presentInviteUsersScreen)
case .leftRoom: case .leftRoom:
actionsSubject.send(.leftRoom) actionsSubject.send(.leftRoom)
case .requestEditDetailsPresentation(let accountOwner): case .requestEditDetailsPresentation:
actionsSubject.send(.presentRoomDetailsEditScreen(accountOwner: accountOwner)) actionsSubject.send(.presentRoomDetailsEditScreen)
case .requestNotificationSettingsPresentation: case .requestNotificationSettingsPresentation:
actionsSubject.send(.presentNotificationSettingsScreen) actionsSubject.send(.presentNotificationSettingsScreen)
case .requestPollsHistoryPresentation: case .requestPollsHistoryPresentation:
@@ -26,7 +26,7 @@ enum RoomDetailsScreenViewModelAction {
case requestMemberDetailsPresentation case requestMemberDetailsPresentation
case requestInvitePeoplePresentation case requestInvitePeoplePresentation
case leftRoom case leftRoom
case requestEditDetailsPresentation(RoomMemberProxyProtocol) case requestEditDetailsPresentation
case requestPollsHistoryPresentation case requestPollsHistoryPresentation
} }
@@ -20,18 +20,14 @@ import SwiftUI
typealias RoomDetailsScreenViewModelType = StateStoreViewModel<RoomDetailsScreenViewState, RoomDetailsScreenViewAction> typealias RoomDetailsScreenViewModelType = StateStoreViewModel<RoomDetailsScreenViewState, RoomDetailsScreenViewAction>
class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol { class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol {
private let accountUserID: String
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol
private let clientProxy: ClientProxyProtocol
private let analyticsService: AnalyticsService private let analyticsService: AnalyticsService
private let mediaProvider: MediaProviderProtocol private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationSettingsProxy: NotificationSettingsProxyProtocol private let notificationSettingsProxy: NotificationSettingsProxyProtocol
private let attributedStringBuilder: AttributedStringBuilderProtocol private let attributedStringBuilder: AttributedStringBuilderProtocol
private var accountOwner: RoomMemberProxyProtocol? {
didSet { updatePowerLevelPermissions() }
}
private var dmRecipient: RoomMemberProxyProtocol? private var dmRecipient: RoomMemberProxyProtocol?
private var actionsSubject: PassthroughSubject<RoomDetailsScreenViewModelAction, Never> = .init() private var actionsSubject: PassthroughSubject<RoomDetailsScreenViewModelAction, Never> = .init()
@@ -40,15 +36,15 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(accountUserID: String, init(roomProxy: RoomProxyProtocol,
roomProxy: RoomProxyProtocol, clientProxy: ClientProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
analyticsService: AnalyticsService, analyticsService: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
notificationSettingsProxy: NotificationSettingsProxyProtocol, notificationSettingsProxy: NotificationSettingsProxyProtocol,
attributedStringBuilder: AttributedStringBuilderProtocol) { attributedStringBuilder: AttributedStringBuilderProtocol) {
self.accountUserID = accountUserID
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.clientProxy = clientProxy
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
self.analyticsService = analyticsService self.analyticsService = analyticsService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -69,9 +65,10 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
imageProvider: mediaProvider) imageProvider: mediaProvider)
updateRoomInfo() updateRoomInfo()
Task { await updatePowerLevelPermissions() }
setupRoomSubscription() setupRoomSubscription()
fetchMembers() Task { await fetchMembersIfNeeded() }
setupNotificationSettingsSubscription() setupNotificationSettingsSubscription()
fetchNotificationSettings() fetchNotificationSettings()
@@ -103,11 +100,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
case .processTapUnignore: case .processTapUnignore:
state.bindings.ignoreUserRoomAlertItem = .init(action: .unignore) state.bindings.ignoreUserRoomAlertItem = .init(action: .unignore)
case .processTapEdit, .processTapAddTopic: case .processTapEdit, .processTapAddTopic:
guard let accountOwner else { actionsSubject.send(.requestEditDetailsPresentation)
MXLog.error("Missing account owner when presenting the room's edit details screen")
return
}
actionsSubject.send(.requestEditDetailsPresentation(accountOwner))
case .ignoreConfirmed: case .ignoreConfirmed:
Task { await ignore() } Task { await ignore() }
case .unignoreConfirmed: case .unignoreConfirmed:
@@ -154,13 +147,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
} }
private func fetchMembers() {
Task {
await fetchMembersIfNeeded()
await fetchAccountOwner()
}
}
private func fetchMembersIfNeeded() async { private func fetchMembersIfNeeded() async {
// We need to fetch members just in 1-to-1 chat to get the member object for the other person // We need to fetch members just in 1-to-1 chat to get the member object for the other person
guard roomProxy.isEncryptedOneToOneRoom else { guard roomProxy.isEncryptedOneToOneRoom else {
@@ -169,9 +155,9 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
roomProxy.membersPublisher roomProxy.membersPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] members in .sink { [weak self, ownUserID = roomProxy.ownUserID] members in
guard let self else { return } guard let self else { return }
let dmRecipient = members.first(where: { !$0.isAccountOwner }) let dmRecipient = members.first(where: { $0.userID != ownUserID })
self.dmRecipient = dmRecipient self.dmRecipient = dmRecipient
self.state.dmRecipient = dmRecipient.map(RoomMemberDetails.init(withProxy:)) self.state.dmRecipient = dmRecipient.map(RoomMemberDetails.init(withProxy:))
} }
@@ -180,20 +166,13 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
await roomProxy.updateMembers() await roomProxy.updateMembers()
} }
private func fetchAccountOwner() async { private func updatePowerLevelPermissions() async {
switch await roomProxy.getMember(userID: accountUserID) { async let canInviteUsers = roomProxy.canUserInvite(userID: roomProxy.ownUserID) == .success(true)
case .success(let member): // Can't use async let because the mocks aren't thread safe when calling the same method 🤦‍♂️
accountOwner = member state.canEditRoomName = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName) == .success(true)
case .failure(let error): state.canEditRoomTopic = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomTopic) == .success(true)
MXLog.error("Failed (error: \(error) to get account owner member with id: \(accountUserID), in room: \(roomProxy.id)") state.canEditRoomAvatar = await roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomAvatar) == .success(true)
} state.canInviteUsers = await canInviteUsers
}
private func updatePowerLevelPermissions() {
state.canInviteUsers = accountOwner?.canInviteUsers ?? false
state.canEditRoomName = accountOwner?.canSendStateEvent(type: .roomName) ?? false
state.canEditRoomTopic = accountOwner?.canSendStateEvent(type: .roomTopic) ?? false
state.canEditRoomAvatar = accountOwner?.canSendStateEvent(type: .roomAvatar) ?? false
} }
private func setupNotificationSettingsSubscription() { private func setupNotificationSettingsSubscription() {
@@ -280,8 +259,14 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
private func ignore() async { private func ignore() async {
guard let dmUserID = dmRecipient?.userID else {
MXLog.error("Attempting to ignore a nil DM Recipient")
state.bindings.alertInfo = .init(id: .unknown)
return
}
state.isProcessingIgnoreRequest = true state.isProcessingIgnoreRequest = true
let result = await dmRecipient?.ignoreUser() let result = await clientProxy.ignoreUser(dmUserID)
state.isProcessingIgnoreRequest = false state.isProcessingIgnoreRequest = false
switch result { switch result {
case .success: case .success:
@@ -289,14 +274,20 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
var dmRecipient = state.dmRecipient var dmRecipient = state.dmRecipient
dmRecipient?.isIgnored = true dmRecipient?.isIgnored = true
state.dmRecipient = dmRecipient state.dmRecipient = dmRecipient
case .failure, .none: case .failure:
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
} }
} }
private func unignore() async { private func unignore() async {
guard let dmUserID = dmRecipient?.userID else {
MXLog.error("Attempting to unignore a nil DM Recipient")
state.bindings.alertInfo = .init(id: .unknown)
return
}
state.isProcessingIgnoreRequest = true state.isProcessingIgnoreRequest = true
let result = await dmRecipient?.unignoreUser() let result = await clientProxy.unignoreUser(dmUserID)
state.isProcessingIgnoreRequest = false state.isProcessingIgnoreRequest = false
switch result { switch result {
case .success: case .success:
@@ -304,7 +295,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
var dmRecipient = state.dmRecipient var dmRecipient = state.dmRecipient
dmRecipient?.isIgnored = false dmRecipient?.isIgnored = false
state.dmRecipient = dmRecipient state.dmRecipient = dmRecipient
case .failure, .none: case .failure:
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
} }
} }
@@ -279,7 +279,7 @@ struct RoomDetailsScreen: View {
struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
static let genericRoomViewModel = { static let genericRoomViewModel = {
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
.mockMe, .mockMeAdmin,
.mockAlice, .mockAlice,
.mockBob, .mockBob,
.mockCharlie .mockCharlie
@@ -304,8 +304,8 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration)
let appSettings = AppSettings() let appSettings = AppSettings()
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(roomProxy: roomProxy,
roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -330,8 +330,8 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
let appSettings = AppSettings() let appSettings = AppSettings()
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(roomProxy: roomProxy,
roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -342,7 +342,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
static let simpleRoomViewModel = { static let simpleRoomViewModel = {
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
.mockMe, .mockMeAdmin,
.mockAlice, .mockAlice,
.mockBob, .mockBob,
.mockCharlie .mockCharlie
@@ -355,8 +355,8 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
let appSettings = AppSettings() let appSettings = AppSettings()
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(roomProxy: roomProxy,
roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -18,8 +18,9 @@ import Combine
import SwiftUI import SwiftUI
struct RoomMemberDetailsScreenCoordinatorParameters { struct RoomMemberDetailsScreenCoordinatorParameters {
let roomProxy: RoomProxyProtocol
let userID: String let userID: String
let roomProxy: RoomProxyProtocol
let clientProxy: ClientProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
} }
@@ -39,8 +40,9 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
} }
init(parameters: RoomMemberDetailsScreenCoordinatorParameters) { init(parameters: RoomMemberDetailsScreenCoordinatorParameters) {
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: parameters.roomProxy, viewModel = RoomMemberDetailsScreenViewModel(userID: parameters.userID,
userID: parameters.userID, roomProxy: parameters.roomProxy,
clientProxy: parameters.clientProxy,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
} }
@@ -23,6 +23,7 @@ enum RoomMemberDetailsScreenViewModelAction {
struct RoomMemberDetailsScreenViewState: BindableState { struct RoomMemberDetailsScreenViewState: BindableState {
let userID: String let userID: String
var memberDetails: RoomMemberDetails? var memberDetails: RoomMemberDetails?
var isOwnMemberDetails = false
var isProcessingIgnoreRequest = false var isProcessingIgnoreRequest = false
var bindings: RoomMemberDetailsScreenViewStateBindings var bindings: RoomMemberDetailsScreenViewStateBindings
@@ -21,6 +21,7 @@ typealias RoomMemberDetailsScreenViewModelType = StateStoreViewModel<RoomMemberD
class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, RoomMemberDetailsScreenViewModelProtocol { class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, RoomMemberDetailsScreenViewModelProtocol {
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol
private let clientProxy: ClientProxyProtocol
private let mediaProvider: MediaProviderProtocol private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -32,11 +33,13 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(roomProxy: RoomProxyProtocol, init(userID: String,
userID: String, roomProxy: RoomProxyProtocol,
clientProxy: ClientProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.clientProxy = clientProxy
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -54,6 +57,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
case .success(let member): case .success(let member):
roomMemberProxy = member roomMemberProxy = member
state.memberDetails = RoomMemberDetails(withProxy: member) state.memberDetails = RoomMemberDetails(withProxy: member)
state.isOwnMemberDetails = member.userID == roomProxy.ownUserID
case .failure(let error): case .failure(let error):
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
MXLog.error("[RoomFlowCoordinator] Failed to get member: \(error)") MXLog.error("[RoomFlowCoordinator] Failed to get member: \(error)")
@@ -100,7 +104,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
} }
state.isProcessingIgnoreRequest = true state.isProcessingIgnoreRequest = true
let result = await roomMemberProxy.ignoreUser() let result = await clientProxy.ignoreUser(roomMemberProxy.userID)
state.isProcessingIgnoreRequest = false state.isProcessingIgnoreRequest = false
switch result { switch result {
case .success: case .success:
@@ -118,7 +122,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
} }
state.isProcessingIgnoreRequest = true state.isProcessingIgnoreRequest = true
let result = await roomMemberProxy.unignoreUser() let result = await clientProxy.unignoreUser(roomMemberProxy.userID)
state.isProcessingIgnoreRequest = false state.isProcessingIgnoreRequest = false
switch result { switch result {
case .success: case .success:
@@ -36,8 +36,7 @@ struct RoomMemberDetailsScreen: View {
Form { Form {
headerSection headerSection
if let memberDetails = context.viewState.memberDetails, if context.viewState.memberDetails != nil, !context.viewState.isOwnMemberDetails {
!memberDetails.isAccountOwner {
directChatSection directChatSection
blockUserSection blockUserSection
} }
@@ -124,8 +123,9 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview {
let roomProxyMock = RoomProxyMock(with: .init(name: "")) let roomProxyMock = RoomProxyMock(with: .init(name: ""))
roomProxyMock.getMemberUserIDReturnValue = .success(member) roomProxyMock.getMemberUserIDReturnValue = .success(member)
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, return RoomMemberDetailsScreenViewModel(userID: member.userID,
userID: member.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
@@ -135,8 +135,9 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview {
let roomProxyMock = RoomProxyMock(with: .init(name: "")) let roomProxyMock = RoomProxyMock(with: .init(name: ""))
roomProxyMock.getMemberUserIDReturnValue = .success(member) roomProxyMock.getMemberUserIDReturnValue = .success(member)
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, return RoomMemberDetailsScreenViewModel(userID: member.userID,
userID: member.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
@@ -146,8 +147,9 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview {
let roomProxyMock = RoomProxyMock(with: .init(name: "")) let roomProxyMock = RoomProxyMock(with: .init(name: ""))
roomProxyMock.getMemberUserIDReturnValue = .success(member) roomProxyMock.getMemberUserIDReturnValue = .success(member)
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, return RoomMemberDetailsScreenViewModel(userID: member.userID,
userID: member.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
@@ -106,11 +106,14 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
invitedMembers: roomMembersDetails.invitedMembers, invitedMembers: roomMembersDetails.invitedMembers,
bannedMembers: roomMembersDetails.bannedMembers, bannedMembers: roomMembersDetails.bannedMembers,
bindings: state.bindings) bindings: state.bindings)
if let accountOwner = roomMembersDetails.accountOwner {
self.state.canInviteUsers = accountOwner.canInviteUsers async let canInviteUsers = roomProxy.canUserInvite(userID: roomProxy.ownUserID)
self.state.canKickUsers = accountOwner.canKickUsers async let canKickUsers = roomProxy.canUserKick(userID: roomProxy.ownUserID)
self.state.canBanUsers = accountOwner.canBanUsers async let canBanUsers = roomProxy.canUserBan(userID: roomProxy.ownUserID)
} self.state.canInviteUsers = await canInviteUsers == .success(true)
self.state.canKickUsers = await canKickUsers == .success(true)
self.state.canBanUsers = await canBanUsers == .success(true)
if state.bindings.mode == .banned, roomMembersDetails.bannedMembers.isEmpty { if state.bindings.mode == .banned, roomMembersDetails.bannedMembers.isEmpty {
state.bindings.mode = .members state.bindings.mode = .members
} }
@@ -125,13 +128,8 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
var invitedMembers: [RoomMemberDetails] = .init() var invitedMembers: [RoomMemberDetails] = .init()
var joinedMembers: [RoomMemberDetails] = .init() var joinedMembers: [RoomMemberDetails] = .init()
var bannedMembers: [RoomMemberDetails] = .init() var bannedMembers: [RoomMemberDetails] = .init()
var accountOwner: RoomMemberProxyProtocol?
for member in members { for member in members {
if accountOwner == nil, member.isAccountOwner {
accountOwner = member
}
switch member.membership { switch member.membership {
case .invite: case .invite:
invitedMembers.append(.init(withProxy: member)) invitedMembers.append(.init(withProxy: member))
@@ -146,8 +144,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
return .init(invitedMembers: invitedMembers, return .init(invitedMembers: invitedMembers,
joinedMembers: joinedMembers, joinedMembers: joinedMembers,
bannedMembers: bannedMembers.sorted { $0.id.localizedStandardCompare($1.id) == .orderedAscending }, // Re-sort ignoring display name. bannedMembers: bannedMembers.sorted { $0.id.localizedStandardCompare($1.id) == .orderedAscending }) // Re-sort ignoring display name.
accountOwner: accountOwner)
} }
.value .value
} }
@@ -257,5 +254,4 @@ private struct RoomMembersDetails {
var invitedMembers: [RoomMemberDetails] var invitedMembers: [RoomMemberDetails]
var joinedMembers: [RoomMemberDetails] var joinedMembers: [RoomMemberDetails]
var bannedMembers: [RoomMemberDetails] var bannedMembers: [RoomMemberDetails]
var accountOwner: RoomMemberProxyProtocol?
} }
@@ -142,10 +142,7 @@ struct RoomMembersListScreen_Previews: PreviewProvider, TestablePreview {
initialMode: RoomMembersListScreenMode = .members) -> RoomMembersListScreenViewModel { initialMode: RoomMembersListScreenMode = .members) -> RoomMembersListScreenViewModel {
let mockAdmin = RoomMemberProxyMock.mockAdmin let mockAdmin = RoomMemberProxyMock.mockAdmin
if isAdmin { let ownUserID = isAdmin ? mockAdmin.userID : RoomMemberProxyMock.mockMe.userID
mockAdmin.underlyingCanBanUsers = true
mockAdmin.underlyingIsAccountOwner = true
}
var members: [RoomMemberProxyMock] = [ var members: [RoomMemberProxyMock] = [
.mockAlice, .mockAlice,
@@ -160,7 +157,10 @@ struct RoomMembersListScreen_Previews: PreviewProvider, TestablePreview {
} }
return RoomMembersListScreenViewModel(initialMode: initialMode, return RoomMembersListScreenViewModel(initialMode: initialMode,
roomProxy: RoomProxyMock(with: .init(name: "Some room", members: members)), roomProxy: RoomProxyMock(with: .init(name: "Some room",
members: members,
ownUserID: ownUserID,
canUserInvite: false)),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
@@ -589,7 +589,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
guard guard
let members = await roomProxy.members(), let members = await roomProxy.members(),
members.count == 2, members.count == 2,
let otherPerson = members.first(where: { !$0.isAccountOwner && $0.membership == .leave }) let otherPerson = members.first(where: { $0.userID != roomProxy.ownUserID && $0.membership == .leave })
else { else {
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError) userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
return return
@@ -22,7 +22,7 @@ struct RoomMemberDetails: Identifiable, Hashable {
let name: String? let name: String?
let avatarURL: URL? let avatarURL: URL?
let permalink: URL? let permalink: URL?
let isAccountOwner: Bool
var isIgnored: Bool var isIgnored: Bool
var isBanned: Bool var isBanned: Bool
@@ -36,7 +36,7 @@ extension RoomMemberDetails {
name = proxy.displayName name = proxy.displayName
avatarURL = proxy.avatarURL avatarURL = proxy.avatarURL
permalink = proxy.permalink permalink = proxy.permalink
isAccountOwner = proxy.isAccountOwner
isIgnored = proxy.isIgnored isIgnored = proxy.isIgnored
isBanned = proxy.membership == .ban isBanned = proxy.membership == .ban
role = .init(proxy.role) role = .init(proxy.role)
@@ -47,7 +47,7 @@ extension RoomMemberDetails {
name = nil name = nil
avatarURL = nil avatarURL = nil
permalink = nil permalink = nil
isAccountOwner = false
isIgnored = false isIgnored = false
isBanned = false isBanned = false
role = .user role = .user
@@ -19,69 +19,25 @@ import MatrixRustSDK
final class RoomMemberProxy: RoomMemberProxyProtocol { final class RoomMemberProxy: RoomMemberProxyProtocol {
private let backgroundTaskService: BackgroundTaskServiceProtocol private let backgroundTaskService: BackgroundTaskServiceProtocol
private let member: RoomMemberProtocol private let member: RoomMember
private let backgroundAccountDataTaskName = "SendAccountDataEvent" private let backgroundAccountDataTaskName = "SendAccountDataEvent"
private var sendAccountDataEventBackgroundTask: BackgroundTaskProtocol? private var sendAccountDataEventBackgroundTask: BackgroundTaskProtocol?
private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roommemberproxy.userinitiated", qos: .userInitiated) private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roommemberproxy.userinitiated", qos: .userInitiated)
init(member: RoomMemberProtocol, backgroundTaskService: BackgroundTaskServiceProtocol) { init(member: RoomMember, backgroundTaskService: BackgroundTaskServiceProtocol) {
self.backgroundTaskService = backgroundTaskService self.backgroundTaskService = backgroundTaskService
self.member = member self.member = member
} }
lazy var userID = member.userId() var userID: String { member.userId }
var displayName: String? { member.displayName }
var avatarURL: URL? { member.avatarUrl.flatMap(URL.init(string:)) }
lazy var displayName = member.displayName() var membership: MembershipState { member.membership }
var isIgnored: Bool { member.isIgnored }
lazy var avatarURL = member.avatarUrl().flatMap(URL.init(string:)) var powerLevel: Int { Int(member.powerLevel) }
var role: RoomMemberRole { member.suggestedRoleForPowerLevel }
lazy var membership = member.membership()
lazy var isAccountOwner = member.isAccountUser()
lazy var isIgnored = member.isIgnored()
lazy var powerLevel = Int(member.powerLevel())
lazy var role = member.suggestedRoleForPowerLevel()
lazy var canInviteUsers = member.canInvite()
lazy var canKickUsers = member.canKick()
lazy var canBanUsers = member.canBan()
func canSendStateEvent(type: StateEventType) -> Bool {
member.canSendState(stateEvent: type)
}
func ignoreUser() async -> Result<Void, RoomMemberProxyError> {
sendAccountDataEventBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundAccountDataTaskName, isReusable: true)
defer {
sendAccountDataEventBackgroundTask?.stop()
}
return await Task.dispatch(on: userInitiatedDispatchQueue) {
do {
try self.member.ignore()
return .success(())
} catch {
return .failure(.ignoreUserFailed)
}
}
}
func unignoreUser() async -> Result<Void, RoomMemberProxyError> {
sendAccountDataEventBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundAccountDataTaskName, isReusable: true)
defer {
sendAccountDataEventBackgroundTask?.stop()
}
return await Task.dispatch(on: userInitiatedDispatchQueue) {
do {
try self.member.unignore()
return .success(())
} catch {
return .failure(.unignoreUserFailed)
}
}
}
} }
@@ -27,19 +27,12 @@ protocol RoomMemberProxyProtocol: AnyObject {
var userID: String { get } var userID: String { get }
var displayName: String? { get } var displayName: String? { get }
var avatarURL: URL? { get } var avatarURL: URL? { get }
var membership: MembershipState { get } var membership: MembershipState { get }
var isAccountOwner: Bool { get }
var isIgnored: Bool { get } var isIgnored: Bool { get }
var powerLevel: Int { get } var powerLevel: Int { get }
var role: RoomMemberRole { get } var role: RoomMemberRole { get }
var canInviteUsers: Bool { get }
var canKickUsers: Bool { get }
var canBanUsers: Bool { get }
func ignoreUser() async -> Result<Void, RoomMemberProxyError>
func unignoreUser() async -> Result<Void, RoomMemberProxyError>
func canSendStateEvent(type: StateEventType) -> Bool
} }
extension RoomMemberProxyProtocol { extension RoomMemberProxyProtocol {
+36 -14
View File
@@ -234,20 +234,6 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
do {
try await room.ignoreUser(userId: userID)
return .success(())
} catch {
return .failure(.failedIgnoringUser)
}
}
func leaveRoom() async -> Result<Void, RoomProxyError> { func leaveRoom() async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer { defer {
@@ -409,6 +395,24 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func canUser(userID: String, sendStateEvent event: StateEventType) async -> Result<Bool, RoomProxyError> {
do {
return try await .success(room.canUserSendState(userId: userID, stateEvent: event))
} catch {
MXLog.error("Failed checking if the user can send \(event) with error: \(error)")
return .failure(.failedCheckingPermission)
}
}
func canUserInvite(userID: String) async -> Result<Bool, RoomProxyError> {
do {
return try await .success(room.canUserInvite(userId: userID))
} catch {
MXLog.error("Failed checking if the user can invite with error: \(error)")
return .failure(.failedCheckingPermission)
}
}
func canUserRedactOther(userID: String) async -> Result<Bool, RoomProxyError> { func canUserRedactOther(userID: String) async -> Result<Bool, RoomProxyError> {
do { do {
return try await .success(room.canUserRedactOther(userId: userID)) return try await .success(room.canUserRedactOther(userId: userID))
@@ -427,6 +431,24 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func canUserKick(userID: String) async -> Result<Bool, RoomProxyError> {
do {
return try await .success(room.canUserKick(userId: userID))
} catch {
MXLog.error("Failed checking if the user can kick with error: \(error)")
return .failure(.failedCheckingPermission)
}
}
func canUserBan(userID: String) async -> Result<Bool, RoomProxyError> {
do {
return try await .success(room.canUserBan(userId: userID))
} catch {
MXLog.error("Failed checking if the user can ban with error: \(error)")
return .failure(.failedCheckingPermission)
}
}
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> { func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> {
do { do {
return try await .success(room.canUserTriggerRoomNotification(userId: userID)) return try await .success(room.canUserTriggerRoomNotification(userId: userID))
@@ -83,8 +83,6 @@ protocol RoomProxyProtocol {
func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError> func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError>
func ignoreUser(_ userID: String) async -> Result<Void, RoomProxyError>
func leaveRoom() async -> Result<Void, RoomProxyError> func leaveRoom() async -> Result<Void, RoomProxyError>
func updateMembers() async func updateMembers() async
@@ -120,8 +118,12 @@ protocol RoomProxyProtocol {
func currentPowerLevelChanges() async -> Result<RoomPowerLevelChanges, RoomProxyError> func currentPowerLevelChanges() async -> Result<RoomPowerLevelChanges, RoomProxyError>
func applyPowerLevelChanges(_ changes: RoomPowerLevelChanges) async -> Result<Void, RoomProxyError> func applyPowerLevelChanges(_ changes: RoomPowerLevelChanges) async -> Result<Void, RoomProxyError>
func canUser(userID: String, sendStateEvent event: StateEventType) async -> Result<Bool, RoomProxyError>
func canUserInvite(userID: String) async -> Result<Bool, RoomProxyError>
func canUserRedactOther(userID: String) async -> Result<Bool, RoomProxyError> func canUserRedactOther(userID: String) async -> Result<Bool, RoomProxyError>
func canUserRedactOwn(userID: String) async -> Result<Bool, RoomProxyError> func canUserRedactOwn(userID: String) async -> Result<Bool, RoomProxyError>
func canUserKick(userID: String) async -> Result<Bool, RoomProxyError>
func canUserBan(userID: String) async -> Result<Bool, RoomProxyError>
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
// MARK: - Moderation // MARK: - Moderation
@@ -586,9 +586,10 @@ class MockScreen: Identifiable {
name: "Room", name: "Room",
isEncrypted: true, isEncrypted: true,
members: members, members: members,
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false))) memberForID: .mockMe,
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", canUserInvite: false))
roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -607,9 +608,10 @@ class MockScreen: Identifiable {
isEncrypted: true, isEncrypted: true,
canonicalAlias: "#mock:room.org", canonicalAlias: "#mock:room.org",
members: members, members: members,
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false))) memberForID: .mockMe,
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", canUserInvite: false))
roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -620,8 +622,7 @@ class MockScreen: Identifiable {
return navigationStackCoordinator return navigationStackCoordinator
case .roomDetailsScreenWithEmptyTopic: case .roomDetailsScreenWithEmptyTopic:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomTopic], canInviteUsers: false) let members: [RoomMemberProxyMock] = [.mockMeAdmin, .mockBob, .mockCharlie]
let members: [RoomMemberProxyMock] = [owner, .mockBob, .mockCharlie]
let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier", let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier",
name: "Room", name: "Room",
topic: nil, topic: nil,
@@ -630,9 +631,10 @@ class MockScreen: Identifiable {
isEncrypted: true, isEncrypted: true,
canonicalAlias: "#mock:room.org", canonicalAlias: "#mock:room.org",
members: members, members: members,
memberForID: owner)) memberForID: .mockMeAdmin,
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", canUserInvite: false))
roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -643,15 +645,16 @@ class MockScreen: Identifiable {
return navigationStackCoordinator return navigationStackCoordinator
case .roomDetailsScreenWithInvite: case .roomDetailsScreenWithInvite:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [], canInviteUsers: true) let owner: RoomMemberProxyMock = .mockMe
let members: [RoomMemberProxyMock] = [owner, .mockBob, .mockCharlie] let members: [RoomMemberProxyMock] = [owner, .mockBob, .mockCharlie]
let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier", let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier",
name: "Room", name: "Room",
isEncrypted: true, isEncrypted: true,
members: members, members: members,
memberForID: owner)) memberForID: owner,
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", canUserInvite: true))
roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -669,9 +672,10 @@ class MockScreen: Identifiable {
isDirect: true, isDirect: true,
isEncrypted: true, isEncrypted: true,
members: members, members: members,
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false))) memberForID: .mockMe,
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", canUserInvite: false))
roomProxy: roomProxy, let coordinator = RoomDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -681,16 +685,16 @@ class MockScreen: Identifiable {
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .roomEditDetails, .roomEditDetailsReadOnly: case .roomEditDetails, .roomEditDetailsReadOnly:
let allowedStateEvents: [StateEventType] = id == .roomEditDetails ? [.roomAvatar, .roomName, .roomTopic] : [] let members: [RoomMemberProxyMock] = id == .roomEditDetails ? [.mockMeAdmin] : [.mockMe]
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier", let roomProxy = RoomProxyMock(with: .init(id: "MockRoomIdentifier",
name: "Room", name: "Room",
topic: "What a cool topic!", topic: "What a cool topic!",
avatarURL: .picturesDirectory)) avatarURL: .picturesDirectory,
let coordinator = RoomDetailsEditScreenCoordinator(parameters: .init(accountOwner: RoomMemberProxyMock.mockOwner(allowedStateEvents: allowedStateEvents), members: members))
let coordinator = RoomDetailsEditScreenCoordinator(parameters: .init(roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
roomProxy: roomProxy,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
orientationManager: OrientationManagerMock())) orientationManager: OrientationManagerMock()))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
@@ -734,6 +738,7 @@ class MockScreen: Identifiable {
let coordinator = ReportContentScreenCoordinator(parameters: .init(eventID: "test", let coordinator = ReportContentScreenCoordinator(parameters: .init(eventID: "test",
senderID: RoomMemberProxyMock.mockAlice.userID, senderID: RoomMemberProxyMock.mockAlice.userID,
roomProxy: RoomProxyMock(with: .init(name: "test")), roomProxy: RoomProxyMock(with: .init(name: "test")),
clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock())) userIndicatorController: UserIndicatorControllerMock()))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
@@ -774,8 +779,9 @@ class MockScreen: Identifiable {
roomProxy.getMemberUserIDReturnValue = .success(member) roomProxy.getMemberUserIDReturnValue = .success(member)
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(userID: member.userID,
userID: member.userID, roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
@@ -786,8 +792,9 @@ class MockScreen: Identifiable {
roomProxy.getMemberUserIDReturnValue = .success(member) roomProxy.getMemberUserIDReturnValue = .success(member)
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(userID: member.userID,
userID: member.userID, roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
@@ -798,8 +805,9 @@ class MockScreen: Identifiable {
roomProxy.getMemberUserIDReturnValue = .success(member) roomProxy.getMemberUserIDReturnValue = .success(member)
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(userID: member.userID,
userID: member.userID, roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
@@ -27,7 +27,7 @@ final class CompletionSuggestionServiceTests: XCTestCase {
cancellables.removeAll() cancellables.removeAll()
} }
func testUserSuggestons() async throws { func testUserSuggestions() 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(name: "test", members: members)) let roomProxyMock = RoomProxyMock(with: .init(name: "test", members: members))
@@ -64,7 +64,7 @@ final class CompletionSuggestionServiceTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
} }
func testUserSuggestonsIncludingAllUsers() async throws { func testUserSuggestionsIncludingAllUsers() 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(name: "test", members: members, canUserTriggerRoomNotification: true)) let roomProxyMock = RoomProxyMock(with: .init(name: "test", members: members, canUserTriggerRoomNotification: true))
@@ -27,9 +27,11 @@ class ReportContentScreenViewModelTests: XCTestCase {
// Given the report content view for some content. // Given the report content view for some content.
let roomProxy = RoomProxyMock(with: .init(name: "test")) let roomProxy = RoomProxyMock(with: .init(name: "test"))
roomProxy.reportContentReasonReturnValue = .success(()) roomProxy.reportContentReasonReturnValue = .success(())
let clientProxy = ClientProxyMock(.init())
let viewModel = ReportContentScreenViewModel(eventID: eventID, let viewModel = ReportContentScreenViewModel(eventID: eventID,
senderID: senderID, senderID: senderID,
roomProxy: roomProxy) roomProxy: roomProxy,
clientProxy: clientProxy)
// When reporting the content without ignoring the user. // When reporting the content without ignoring the user.
viewModel.state.bindings.reasonText = reportReason viewModel.state.bindings.reasonText = reportReason
@@ -46,18 +48,19 @@ class ReportContentScreenViewModelTests: XCTestCase {
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.") XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, eventID, "The event ID should match the content being reported.") XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, eventID, "The event ID should match the content being reported.")
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.") XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.")
XCTAssertEqual(roomProxy.ignoreUserCallsCount, 0, "A call to ignore a user should not have been made.") XCTAssertEqual(clientProxy.ignoreUserCallsCount, 0, "A call to ignore a user should not have been made.")
XCTAssertNil(roomProxy.ignoreUserReceivedUserID, "The sender shouldn't have been ignored.") XCTAssertNil(clientProxy.ignoreUserReceivedUserID, "The sender shouldn't have been ignored.")
} }
func testReportIgnoringSender() async throws { func testReportIgnoringSender() async throws {
// Given the report content view for some content. // Given the report content view for some content.
let roomProxy = RoomProxyMock(with: .init(name: "test")) let roomProxy = RoomProxyMock(with: .init(name: "test"))
roomProxy.reportContentReasonReturnValue = .success(()) roomProxy.reportContentReasonReturnValue = .success(())
roomProxy.ignoreUserReturnValue = .success(()) let clientProxy = ClientProxyMock(.init())
let viewModel = ReportContentScreenViewModel(eventID: eventID, let viewModel = ReportContentScreenViewModel(eventID: eventID,
senderID: senderID, senderID: senderID,
roomProxy: roomProxy) roomProxy: roomProxy,
clientProxy: clientProxy)
// When reporting the content and also ignoring the user. // When reporting the content and also ignoring the user.
viewModel.state.bindings.reasonText = reportReason viewModel.state.bindings.reasonText = reportReason
@@ -75,7 +78,7 @@ class ReportContentScreenViewModelTests: XCTestCase {
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.") XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, eventID, "The event ID should match the content being reported.") XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.eventID, eventID, "The event ID should match the content being reported.")
XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.") XCTAssertEqual(roomProxy.reportContentReasonReceivedArguments?.reason, reportReason, "The reason should match the user input.")
XCTAssertEqual(roomProxy.ignoreUserCallsCount, 1, "A call should have been made to ignore the sender.") XCTAssertEqual(clientProxy.ignoreUserCallsCount, 1, "A call should have been made to ignore the sender.")
XCTAssertEqual(roomProxy.ignoreUserReceivedUserID, senderID, "The ignored user ID should match the sender.") XCTAssertEqual(clientProxy.ignoreUserReceivedUserID, senderID, "The ignored user ID should match the sender.")
} }
} }
@@ -30,61 +30,57 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
} }
func testCannotSaveOnLanding() { func testCannotSaveOnLanding() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
XCTAssertFalse(context.viewState.canSave) XCTAssertFalse(context.viewState.canSave)
} }
func testCanEdit() { func testCanEdit() async throws {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
let deferred = deferFulfillment(context.$viewState) { $0.canEditName }
try await deferred.fulfill()
XCTAssertTrue(context.viewState.canEditAvatar) XCTAssertTrue(context.viewState.canEditAvatar)
XCTAssertTrue(context.viewState.canEditName) XCTAssertTrue(context.viewState.canEditName)
XCTAssertTrue(context.viewState.canEditTopic) XCTAssertTrue(context.viewState.canEditTopic)
} }
func testCannotEdit() { func testCannotEdit() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: []), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMe]))
roomProxyConfiguration: .init(name: "Some room"))
XCTAssertFalse(context.viewState.canEditAvatar) XCTAssertFalse(context.viewState.canEditAvatar)
XCTAssertFalse(context.viewState.canEditName) XCTAssertFalse(context.viewState.canEditName)
XCTAssertFalse(context.viewState.canEditTopic) XCTAssertFalse(context.viewState.canEditTopic)
} }
func testNameDidChange() { func testNameDidChange() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
context.name = "name" context.name = "name"
XCTAssertTrue(context.viewState.nameDidChange) XCTAssertTrue(context.viewState.nameDidChange)
XCTAssertTrue(context.viewState.canSave) XCTAssertTrue(context.viewState.canSave)
} }
func testTopicDidChange() { func testTopicDidChange() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
context.topic = "topic" context.topic = "topic"
XCTAssertTrue(context.viewState.topicDidChange) XCTAssertTrue(context.viewState.topicDidChange)
XCTAssertTrue(context.viewState.canSave) XCTAssertTrue(context.viewState.canSave)
} }
func testAvatarDidChange() { func testAvatarDidChange() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", avatarURL: .picturesDirectory, members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room", avatarURL: .picturesDirectory))
context.send(viewAction: .removeImage) context.send(viewAction: .removeImage)
XCTAssertTrue(context.viewState.avatarDidChange) XCTAssertTrue(context.viewState.avatarDidChange)
XCTAssertTrue(context.viewState.canSave) XCTAssertTrue(context.viewState.canSave)
} }
func testEmptyNameCannotBeSaved() { func testEmptyNameCannotBeSaved() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
context.name = "" context.name = ""
XCTAssertFalse(context.viewState.canSave) XCTAssertFalse(context.viewState.canSave)
} }
func testSaveShowsSheet() { func testSaveShowsSheet() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
context.name = "name" context.name = "name"
XCTAssertFalse(context.showMediaSheet) XCTAssertFalse(context.showMediaSheet)
context.send(viewAction: .presentMediaSource) context.send(viewAction: .presentMediaSource)
@@ -92,8 +88,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
} }
func testSaveTriggersViewModelAction() async throws { func testSaveTriggersViewModelAction() async throws {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
let deferred = deferFulfillment(viewModel.actions) { action in let deferred = deferFulfillment(viewModel.actions) { action in
action == .saveFinished action == .saveFinished
@@ -107,16 +102,14 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
} }
func testErrorShownOnFailedFetchOfMedia() async throws { func testErrorShownOnFailedFetchOfMedia() async throws {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room"))
viewModel.didSelectMediaUrl(url: .picturesDirectory) viewModel.didSelectMediaUrl(url: .picturesDirectory)
try? await Task.sleep(for: .milliseconds(100)) try? await Task.sleep(for: .milliseconds(100))
XCTAssertNotNil(userIndicatorController.alertInfo) XCTAssertNotNil(userIndicatorController.alertInfo)
} }
func testDeleteAvatar() { func testDeleteAvatar() {
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), setupViewModel(roomProxyConfiguration: .init(name: "Some room", avatarURL: .picturesDirectory, members: [.mockMeAdmin]))
roomProxyConfiguration: .init(name: "Some room", avatarURL: .picturesDirectory))
XCTAssertNotNil(context.viewState.avatarURL) XCTAssertNotNil(context.viewState.avatarURL)
context.send(viewAction: .removeImage) context.send(viewAction: .removeImage)
XCTAssertNil(context.viewState.avatarURL) XCTAssertNil(context.viewState.avatarURL)
@@ -124,11 +117,10 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
// MARK: - Private // MARK: - Private
private func setupViewModel(accountOwner: RoomMemberProxyMock, roomProxyConfiguration: RoomProxyMockConfiguration) { private func setupViewModel(roomProxyConfiguration: RoomProxyMockConfiguration) {
userIndicatorController = UserIndicatorControllerMock.default userIndicatorController = UserIndicatorControllerMock.default
viewModel = .init(accountOwner: accountOwner, viewModel = .init(roomProxy: RoomProxyMock(with: roomProxyConfiguration),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
roomProxy: RoomProxyMock(with: roomProxyConfiguration),
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
} }
} }
@@ -33,8 +33,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
cancellables.removeAll() cancellables.removeAll()
roomProxyMock = RoomProxyMock(with: .init(name: "Test")) roomProxyMock = RoomProxyMock(with: .init(name: "Test"))
notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()) notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -48,8 +48,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testLeaveRoomTappedWhenPublic() async throws { func testLeaveRoomTappedWhenPublic() async throws {
let mockedMembers: [RoomMemberProxyMock] = [.mockBob, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockBob, .mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -70,8 +70,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testLeaveRoomTappedWhenRoomNotPublic() async throws { func testLeaveRoomTappedWhenRoomNotPublic() async throws {
let mockedMembers: [RoomMemberProxyMock] = [.mockBob, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockBob, .mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: false, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: false, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -93,8 +93,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testLeaveRoomTappedWithLessThanTwoMembers() async { func testLeaveRoomTappedWithLessThanTwoMembers() async {
let mockedMembers: [RoomMemberProxyMock] = [.mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: false, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: false, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -146,8 +146,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
let recipient = RoomMemberProxyMock.mockDan let recipient = RoomMemberProxyMock.mockDan
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -166,15 +166,11 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testIgnoreSuccess() async throws { func testIgnoreSuccess() async throws {
let recipient = RoomMemberProxyMock.mockDan let recipient = RoomMemberProxyMock.mockDan
recipient.ignoreUserClosure = {
try? await Task.sleep(for: .milliseconds(100))
return .success(())
}
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -203,14 +199,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testIgnoreFailure() async throws { func testIgnoreFailure() async throws {
let recipient = RoomMemberProxyMock.mockDan let recipient = RoomMemberProxyMock.mockDan
recipient.ignoreUserClosure = {
try? await Task.sleep(for: .milliseconds(100))
return .failure(.ignoreUserFailed)
}
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
let clientProxy = ClientProxyMock(.init())
clientProxy.ignoreUserReturnValue = .failure(.failedIgnoringUser)
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: clientProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -240,14 +234,10 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testUnignoreSuccess() async throws { func testUnignoreSuccess() async throws {
let recipient = RoomMemberProxyMock.mockIgnored let recipient = RoomMemberProxyMock.mockIgnored
recipient.unignoreUserClosure = {
try? await Task.sleep(for: .milliseconds(100))
return .success(())
}
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -276,14 +266,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testUnignoreFailure() async throws { func testUnignoreFailure() async throws {
let recipient = RoomMemberProxyMock.mockIgnored let recipient = RoomMemberProxyMock.mockIgnored
recipient.unignoreUserClosure = {
try? await Task.sleep(for: .milliseconds(100))
return .failure(.unignoreUserFailed)
}
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
let clientProxy = ClientProxyMock(.init())
clientProxy.unignoreUserReturnValue = .failure(.failedUnignoringUser)
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: clientProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -312,13 +300,13 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCannotInvitePeople() async { func testCannotInvitePeople() async {
let mockedMembers: [RoomMemberProxyMock] = [.mockBob, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", roomProxyMock = RoomProxyMock(with: .init(name: "Test",
isPublic: true, isPublic: true,
members: mockedMembers, members: mockedMembers,
memberForID: .mockOwner(allowedStateEvents: [], canInviteUsers: false))) canUserInvite: false))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -334,8 +322,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testInvitePeople() async { func testInvitePeople() async {
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: true, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isPublic: true, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -365,11 +353,13 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCanEditAvatar() async { func testCanEditAvatar() async {
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomAvatar]) let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice] roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers, memberForID: owner)) roomProxyMock.canUserUserIDSendStateEventClosure = { _, event in
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", .success(event == .roomAvatar)
roomProxy: roomProxyMock, }
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -386,11 +376,13 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCanEditName() async { func testCanEditName() async {
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomName]) let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice] roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers, memberForID: owner)) roomProxyMock.canUserUserIDSendStateEventClosure = { _, event in
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", .success(event == .roomName)
roomProxy: roomProxyMock, }
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -407,11 +399,13 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCanEditTopic() async { func testCanEditTopic() async {
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomTopic]) let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice] roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers, memberForID: owner)) roomProxyMock.canUserUserIDSendStateEventClosure = { _, event in
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", .success(event == .roomTopic)
roomProxy: roomProxyMock, }
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -428,11 +422,10 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCannotEditRoom() async { func testCannotEditRoom() async {
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: []) let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockBob, .mockAlice]
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice] roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers))
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers, memberForID: owner)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", clientProxy: ClientProxyMock(.init()),
roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -449,10 +442,10 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testCannotEditDirectRoom() async { func testCannotEditDirectRoom() async {
let mockedMembers: [RoomMemberProxyMock] = [.mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]), .mockBob, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockMeAdmin, .mockBob, .mockAlice]
roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isPublic: false, members: mockedMembers)) roomProxyMock = RoomProxyMock(with: .init(name: "Test", isDirect: true, isPublic: false, members: mockedMembers))
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -469,8 +462,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testNotificationLoadingSettingsFailure() async throws { func testNotificationLoadingSettingsFailure() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneThrowableError = NotificationSettingsError.Generic(msg: "error") notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneThrowableError = NotificationSettingsError.Generic(msg: "error")
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
@@ -35,8 +35,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testInitialState() async throws { func testInitialState() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockAlice roomMemberProxyMock = RoomMemberProxyMock.mockAlice
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -50,12 +51,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testIgnoreSuccess() async throws { func testIgnoreSuccess() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockAlice roomMemberProxyMock = RoomMemberProxyMock.mockAlice
roomMemberProxyMock.ignoreUserClosure = { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
try? await Task.sleep(for: .milliseconds(100)) roomProxy: roomProxyMock,
return .success(()) clientProxy: ClientProxyMock(.init()),
}
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock,
userID: roomMemberProxyMock.userID,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -88,12 +86,11 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testIgnoreFailure() async throws { func testIgnoreFailure() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockAlice roomMemberProxyMock = RoomMemberProxyMock.mockAlice
roomMemberProxyMock.ignoreUserClosure = { let clientProxy = ClientProxyMock(.init())
try? await Task.sleep(for: .milliseconds(100)) clientProxy.ignoreUserReturnValue = .failure(.failedIgnoringUser)
return .failure(.ignoreUserFailed) viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
} roomProxy: roomProxyMock,
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: clientProxy,
userID: roomMemberProxyMock.userID,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -126,13 +123,10 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testUnignoreSuccess() async throws { func testUnignoreSuccess() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockIgnored roomMemberProxyMock = RoomMemberProxyMock.mockIgnored
roomMemberProxyMock.unignoreUserClosure = {
try? await Task.sleep(for: .milliseconds(100))
return .success(())
}
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -163,12 +157,11 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testUnignoreFailure() async throws { func testUnignoreFailure() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockIgnored roomMemberProxyMock = RoomMemberProxyMock.mockIgnored
roomMemberProxyMock.unignoreUserClosure = { let clientProxy = ClientProxyMock(.init())
try? await Task.sleep(for: .milliseconds(100)) clientProxy.unignoreUserReturnValue = .failure(.failedUnignoringUser)
return .failure(.unignoreUserFailed) viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
} roomProxy: roomProxyMock,
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: clientProxy,
userID: roomMemberProxyMock.userID,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -201,8 +194,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testInitialStateAccountOwner() async throws { func testInitialStateAccountOwner() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockMe roomMemberProxyMock = RoomMemberProxyMock.mockMe
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
@@ -216,8 +210,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
func testInitialStateIgnoredUser() async throws { func testInitialStateIgnoredUser() async throws {
roomMemberProxyMock = RoomMemberProxyMock.mockIgnored roomMemberProxyMock = RoomMemberProxyMock.mockIgnored
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock,
clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
+1 -1
View File
@@ -48,7 +48,7 @@ packages:
# Element/Matrix dependencies # Element/Matrix dependencies
MatrixRustSDK: MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.1.44 exactVersion: 1.1.45
# path: ../matrix-rust-sdk # path: ../matrix-rust-sdk
Compound: Compound:
url: https://github.com/element-hq/compound-ios url: https://github.com/element-hq/compound-ios