diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index d3d01337c3..30a2041243 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -540,7 +540,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } private func presentRoomMemberDetails(member: RoomMemberProxyProtocol) { - let params = RoomMemberDetailsScreenCoordinatorParameters(roomMemberProxy: member, mediaProvider: userSession.mediaProvider) + guard let roomProxy else { + fatalError() + } + let params = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: roomProxy, roomMemberProxy: member, mediaProvider: userSession.mediaProvider) let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params) navigationStackCoordinator.push(coordinator) { [weak self] in diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenCoordinator.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenCoordinator.swift index 3697c543d4..0255a40981 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenCoordinator.swift @@ -17,6 +17,7 @@ import SwiftUI struct RoomMemberDetailsScreenCoordinatorParameters { + let roomProxy: RoomProxyProtocol let roomMemberProxy: RoomMemberProxyProtocol let mediaProvider: MediaProviderProtocol } @@ -32,7 +33,9 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol { init(parameters: RoomMemberDetailsScreenCoordinatorParameters) { self.parameters = parameters - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: parameters.roomMemberProxy, mediaProvider: parameters.mediaProvider) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: parameters.roomProxy, + roomMemberProxy: parameters.roomMemberProxy, + mediaProvider: parameters.mediaProvider) } func start() { } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift index 6b825cd718..4735a700e8 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift @@ -19,11 +19,13 @@ import SwiftUI typealias RoomMemberDetailsScreenViewModelType = StateStoreViewModel class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, RoomMemberDetailsScreenViewModelProtocol { - let roomMemberProxy: RoomMemberProxyProtocol + private let roomProxy: RoomProxyProtocol + private let roomMemberProxy: RoomMemberProxyProtocol var callback: ((RoomMemberDetailsScreenViewModelAction) -> Void)? - init(roomMemberProxy: RoomMemberProxyProtocol, mediaProvider: MediaProviderProtocol) { + init(roomProxy: RoomProxyProtocol, roomMemberProxy: RoomMemberProxyProtocol, mediaProvider: MediaProviderProtocol) { + self.roomProxy = roomProxy self.roomMemberProxy = roomMemberProxy let initialViewState = RoomMemberDetailsScreenViewState(details: RoomMemberDetails(withProxy: roomMemberProxy), bindings: .init()) @@ -55,6 +57,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro switch result { case .success: state.details.isIgnored = true + updateMembers() case .failure: state.bindings.alertInfo = .init(id: .unknown) } @@ -68,8 +71,15 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro switch result { case .success: state.details.isIgnored = false + updateMembers() case .failure: state.bindings.alertInfo = .init(id: .unknown) } } + + private func updateMembers() { + Task.detached { + await self.roomProxy.updateMembers() + } + } } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 2cb9862ed8..b5a5c58d58 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -98,19 +98,20 @@ struct RoomMemberDetailsScreen: View { // MARK: - Previews struct RoomMemberDetailsScreen_Previews: PreviewProvider { + static let roomProxyMock = RoomProxyMock(with: .init(displayName: "")) static let otherUserViewModel = { let member = RoomMemberProxyMock.mockDan - return RoomMemberDetailsScreenViewModel(roomMemberProxy: member, mediaProvider: MockMediaProvider()) + return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider()) }() static let accountOwnerViewModel = { let member = RoomMemberProxyMock.mockMe - return RoomMemberDetailsScreenViewModel(roomMemberProxy: member, mediaProvider: MockMediaProvider()) + return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider()) }() static let ignoredUserViewModel = { let member = RoomMemberProxyMock.mockIgnored - return RoomMemberDetailsScreenViewModel(roomMemberProxy: member, mediaProvider: MockMediaProvider()) + return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider()) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenCoordinator.swift b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenCoordinator.swift index ddb7f6e985..124813a76d 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenCoordinator.swift @@ -63,7 +63,7 @@ final class RoomMembersListScreenCoordinator: CoordinatorProtocol { // MARK: - Private private func selectMember(_ member: RoomMemberProxyProtocol) { - let parameters = RoomMemberDetailsScreenCoordinatorParameters(roomMemberProxy: member, mediaProvider: parameters.mediaProvider) + let parameters = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: parameters.roomProxy, roomMemberProxy: member, mediaProvider: parameters.mediaProvider) let coordinator = RoomMemberDetailsScreenCoordinator(parameters: parameters) navigationStackCoordinator?.push(coordinator) diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift index 41fdee8869..c67b10b576 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift @@ -64,7 +64,6 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe roomProxy.membersPublisher .filter { !$0.isEmpty } - .first() .sink { [weak self] members in self?.updateState(members: members) } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 5b864863ce..0fbbbd66b3 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -508,17 +508,17 @@ class MockScreen: Identifiable { return navigationStackCoordinator case .roomMemberDetailsAccountOwner: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockMe, mediaProvider: MockMediaProvider())) + let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockMe, mediaProvider: MockMediaProvider())) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomMemberDetails: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockAlice, mediaProvider: MockMediaProvider())) + let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockAlice, mediaProvider: MockMediaProvider())) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomMemberDetailsIgnoredUser: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomMemberProxy: RoomMemberProxyMock.mockIgnored, mediaProvider: MockMediaProvider())) + let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockIgnored, mediaProvider: MockMediaProvider())) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .invitesWithBadges: diff --git a/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift b/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift index dd4836bb53..057042ae11 100644 --- a/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift +++ b/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift @@ -21,12 +21,19 @@ import XCTest @MainActor class RoomMemberDetailsViewModelTests: XCTestCase { var viewModel: RoomMemberDetailsScreenViewModelProtocol! + var roomProxyMock: RoomProxyMock! var roomMemberProxyMock: RoomMemberProxyMock! var context: RoomMemberDetailsScreenViewModelType.Context { viewModel.context } + override func setUp() async throws { + roomProxyMock = RoomProxyMock(with: .init(displayName: "")) + } + func testInitialState() async { roomMemberProxyMock = RoomMemberProxyMock.mockAlice - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) XCTAssertEqual(context.viewState.details, RoomMemberDetails(withProxy: roomMemberProxyMock)) XCTAssertNil(context.ignoreUserAlert) @@ -39,7 +46,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase { try? await Task.sleep(for: .milliseconds(100)) return .success(()) } - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) context.send(viewAction: .showIgnoreAlert) XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore)) @@ -53,6 +62,8 @@ class RoomMemberDetailsViewModelTests: XCTestCase { XCTAssertEqual(states, [false, true, false]) XCTAssertFalse(context.viewState.isProcessingIgnoreRequest) XCTAssertTrue(context.viewState.details.isIgnored) + try await Task.sleep(for: .microseconds(100)) + XCTAssertTrue(roomProxyMock.updateMembersCalled) } func testIgnoreFailure() async throws { @@ -61,7 +72,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase { try? await Task.sleep(for: .milliseconds(100)) return .failure(.ignoreUserFailed) } - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) context.send(viewAction: .showIgnoreAlert) XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore)) @@ -74,6 +87,8 @@ class RoomMemberDetailsViewModelTests: XCTestCase { XCTAssertEqual(states, [false, true, false]) XCTAssertNotNil(context.alertInfo) XCTAssertFalse(context.viewState.details.isIgnored) + try await Task.sleep(for: .microseconds(100)) + XCTAssertFalse(roomProxyMock.updateMembersCalled) } func testUnignoreSuccess() async throws { @@ -82,7 +97,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase { try? await Task.sleep(for: .milliseconds(100)) return .success(()) } - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) context.send(viewAction: .showUnignoreAlert) XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore)) @@ -95,15 +112,20 @@ class RoomMemberDetailsViewModelTests: XCTestCase { let states = try await deferred.fulfill() XCTAssertEqual(states, [false, true, false]) XCTAssertFalse(context.viewState.details.isIgnored) + try await Task.sleep(for: .microseconds(100)) + XCTAssertTrue(roomProxyMock.updateMembersCalled) } func testUnignoreFailure() async throws { + roomProxyMock = RoomProxyMock(with: .init(displayName: "")) roomMemberProxyMock = RoomMemberProxyMock.mockIgnored roomMemberProxyMock.unignoreUserClosure = { try? await Task.sleep(for: .milliseconds(100)) return .failure(.unignoreUserFailed) } - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) context.send(viewAction: .showUnignoreAlert) XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore)) @@ -117,11 +139,15 @@ class RoomMemberDetailsViewModelTests: XCTestCase { XCTAssertEqual(states, [false, true, false]) XCTAssertTrue(context.viewState.details.isIgnored) XCTAssertNotNil(context.alertInfo) + try await Task.sleep(for: .microseconds(100)) + XCTAssertFalse(roomProxyMock.updateMembersCalled) } func testInitialStateAccountOwner() async { roomMemberProxyMock = RoomMemberProxyMock.mockMe - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) XCTAssertEqual(context.viewState.details, RoomMemberDetails(withProxy: roomMemberProxyMock)) XCTAssertNil(context.ignoreUserAlert) @@ -130,7 +156,9 @@ class RoomMemberDetailsViewModelTests: XCTestCase { func testInitialStateIgnoredUser() async { roomMemberProxyMock = RoomMemberProxyMock.mockIgnored - viewModel = RoomMemberDetailsScreenViewModel(roomMemberProxy: roomMemberProxyMock, mediaProvider: MockMediaProvider()) + viewModel = RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, + roomMemberProxy: roomMemberProxyMock, + mediaProvider: MockMediaProvider()) XCTAssertEqual(context.viewState.details, RoomMemberDetails(withProxy: roomMemberProxyMock)) XCTAssertNil(context.ignoreUserAlert) diff --git a/changelog.d/910.bugfix b/changelog.d/910.bugfix new file mode 100644 index 0000000000..dd1b216ef0 --- /dev/null +++ b/changelog.d/910.bugfix @@ -0,0 +1 @@ +Fix for UI not retaining blocked/unlocked user state after dismissing and re-entering the details from the room member list. \ No newline at end of file