Invite the DM recipient to a new room (#5588)

* handle the details to invite users flow.

* implemented the room creation in the invite screen, and its dismissal + navigaton to the newly created room

* updated preview and unit tests

* pr suggestions
This commit is contained in:
Mauro
2026-05-18 10:08:07 +02:00
committed by GitHub
parent 9ed47c61b7
commit 6c0aaa7a58
39 changed files with 297 additions and 100 deletions
@@ -87,6 +87,8 @@ final class InviteUsersScreenViewModelTests {
switch action {
case .dismiss:
return true
case .openRoom:
return false
}
}
@@ -122,6 +124,8 @@ final class InviteUsersScreenViewModelTests {
switch action {
case .dismiss:
return true
case .openRoom:
return false
}
}
@@ -159,6 +163,74 @@ final class InviteUsersScreenViewModelTests {
try await deferredState.fulfill()
}
// MARK: - Draft (new room)
@Test
func createsNewRoomInDraftMode() async throws {
userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([])
clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
let newRoomID = "!newroom:example.com"
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success(newRoomID)
viewModel = InviteUsersScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
roomType: .draft(mandatoryInvitees: [.mockAlice]),
isSkippable: false,
userDiscoveryService: userDiscoveryService,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings())
// The locked invitee starts pre-selected and locked.
#expect(context.viewState.selectedUsers.map(\.userID) == [UserProfileProxy.mockAlice.userID])
#expect(context.viewState.isInviteeMandatory(.mockAlice))
// The proceed button is disabled while the only selected user is the locked invitee.
#expect(!context.viewState.hasInvitableSelectedUsers)
// Selecting a non-locked user enables the proceed button.
var deferredState = deferFulfillment(viewModel.context.$viewState) { state in
state.isUserSelected(.mockBob)
}
context.send(viewAction: .toggleUser(.mockBob))
try await deferredState.fulfill()
#expect(context.viewState.hasInvitableSelectedUsers)
// Deselecting the non-locked user disables the proceed button again.
deferredState = deferFulfillment(viewModel.context.$viewState) { state in
!state.isUserSelected(.mockBob)
}
context.send(viewAction: .toggleUser(.mockBob))
try await deferredState.fulfill()
#expect(!context.viewState.hasInvitableSelectedUsers)
// Re-select the non-locked user before proceeding.
deferredState = deferFulfillment(viewModel.context.$viewState) { state in
state.isUserSelected(.mockBob)
}
context.send(viewAction: .toggleUser(.mockBob))
try await deferredState.fulfill()
let deferredAction = deferFulfillment(viewModel.actions) { action in
if case .openRoom(let roomID) = action, roomID == newRoomID {
return true
}
return false
}
context.send(viewAction: .proceed)
try await deferredAction.fulfill()
let args = try #require(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments)
#expect(args.name == nil)
#expect(args.topic == nil)
#expect(args.accessType == .private)
#expect(args.isSpace == false)
#expect(args.userIDs == [UserProfileProxy.mockAlice.userID, UserProfileProxy.mockBob.userID])
#expect(args.avatarURL == nil)
#expect(args.aliasLocalPart == nil)
}
// MARK: - Helpers
private func setupViewModel(roomProxy: JoinedRoomProxyProtocol, isSkippable: Bool) {
@@ -168,7 +240,7 @@ final class InviteUsersScreenViewModelTests {
clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
let viewModel = InviteUsersScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
roomProxy: roomProxy,
roomType: .existingRoom(roomProxy: roomProxy),
isSkippable: isSkippable,
userDiscoveryService: userDiscoveryService,
userIndicatorController: UserIndicatorControllerMock(),