Invite people in an existing room (#915)

* Handle power levels

* Add “invite” button in RoomMembersListScreen

* Refactor DI in RoomDetailsScreenCoordinator

* Add navigation to “select people” coordinator

* Add InviteUsersScreenRoomContext

* Add invite user by id api

* Updare InviteUsersScreenRoomContext

* Support disabled state on FormButtonStyles

* Handle membership in UserProfileCell

* Fix conflicts

* Refactor RoomContext -> RoomType

* Refactor UserProfileCell

* Add invite action

* Setup navigation to user flow coordinator

* Add invite logic

* Fix test build errors

* Address comments

* Fix localisations

* Update required state

* Handle error

* Move invite logic in RoomDetailsScreenCoordinator

* Add navigation from room’s members list

* Fix UTs

* Fix missing display name in RoomMemberDetails

* Add user indicator

* Add feature flag

* Add RoomDetailsViewModel UTs

* Add room details ui tests

* Add InviteUsersViewModelTests UTs

* Add UI tests

* Update UI tests

* Empty commit
This commit is contained in:
Alfonso Grillo
2023-05-22 16:03:07 +02:00
committed by GitHub
parent 8ed8cf6f0c
commit d9e0f36aeb
58 changed files with 708 additions and 172 deletions
@@ -22,7 +22,9 @@ struct RoomMembersListScreenCoordinatorParameters {
let members: [RoomMemberProxyProtocol]
}
enum RoomMembersListScreenCoordinatorAction { }
enum RoomMembersListScreenCoordinatorAction {
case invite
}
final class RoomMembersListScreenCoordinator: CoordinatorProtocol {
private let parameters: RoomMembersListScreenCoordinatorParameters
@@ -45,6 +47,8 @@ final class RoomMembersListScreenCoordinator: CoordinatorProtocol {
switch action {
case let .selectMember(member):
self.selectMember(member)
case .invite:
callback?(.invite)
}
}
}
@@ -18,11 +18,13 @@ import Foundation
enum RoomMembersListScreenViewModelAction {
case selectMember(_ member: RoomMemberProxyProtocol)
case invite
}
struct RoomMembersListScreenViewState: BindableState {
private var joinedMembers: [RoomMemberDetails]
private var invitedMembers: [RoomMemberDetails]
var canInviteUsers = false
var bindings: RoomMembersListScreenViewStateBindings
init(joinedMembers: [RoomMemberDetails] = [], invitedMembers: [RoomMemberDetails] = [], bindings: RoomMembersListScreenViewStateBindings = .init()) {
@@ -59,6 +61,7 @@ struct RoomMembersListScreenViewStateBindings {
enum RoomMembersListScreenViewAction {
case selectMember(id: String)
case invite
}
private extension RoomMemberDetails {
@@ -29,7 +29,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
self.members = members
super.init(initialViewState: .init(), imageProvider: mediaProvider)
buildMembersDetails(members: members)
setupState(members: members)
}
// MARK: - Public
@@ -42,12 +42,14 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
return
}
callback?(.selectMember(member))
case .invite:
callback?(.invite)
}
}
// MARK: - Private
func buildMembersDetails(members: [RoomMemberProxyProtocol]) {
private func setupState(members: [RoomMemberProxyProtocol]) {
Task {
let indicatorId = UUID().uuidString
defer {
@@ -55,24 +57,42 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
}
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: indicatorId, type: .modal, title: L10n.commonLoading, persistent: true))
let (invitedMembers, joinedMembers) = await split(members: members)
self.state = .init(joinedMembers: joinedMembers, invitedMembers: invitedMembers)
let roomMembersDetails = await buildMembersDetails(members: members)
self.state = .init(joinedMembers: roomMembersDetails.joinedMembers, invitedMembers: roomMembersDetails.invitedMembers)
self.state.canInviteUsers = roomMembersDetails.accountOwner?.canInviteUsers ?? false
}
}
func split(members: [RoomMemberProxyProtocol]) async -> (invited: [RoomMemberDetails], joined: [RoomMemberDetails]) {
private func buildMembersDetails(members: [RoomMemberProxyProtocol]) async -> RoomMembersDetails {
await Task.detached {
// accessing RoomMember's properties is very slow. We need to do it in a background thread.
let invitedMembers = members
.filter { $0.membership == .invite }
.map(RoomMemberDetails.init(withProxy:))
var invitedMembers: [RoomMemberDetails] = .init()
var joinedMembers: [RoomMemberDetails] = .init()
var accountOwner: RoomMemberProxyProtocol?
let joinedMembers = members
.filter { $0.membership == .join }
.map(RoomMemberDetails.init(withProxy:))
for member in members {
if accountOwner == nil, member.isAccountOwner {
accountOwner = member
}
switch member.membership {
case .invite:
invitedMembers.append(.init(withProxy: member))
case .join:
joinedMembers.append(.init(withProxy: member))
default:
continue
}
}
return (invitedMembers, joinedMembers)
return .init(invitedMembers: invitedMembers, joinedMembers: joinedMembers, accountOwner: accountOwner)
}
.value
}
}
private struct RoomMembersDetails {
var invitedMembers: [RoomMemberDetails]
var joinedMembers: [RoomMemberDetails]
var accountOwner: RoomMemberProxyProtocol?
}
@@ -34,6 +34,11 @@ struct RoomMembersListScreen: View {
.background(Color.element.background.ignoresSafeArea())
.navigationTitle(L10n.commonPeople)
.alert(item: $context.alertInfo) { $0.alert }
.toolbar {
ToolbarItem(placement: .confirmationAction) {
inviteButton
}
}
}
// MARK: - Private
@@ -53,6 +58,17 @@ struct RoomMembersListScreen: View {
}
.padding(.horizontal)
}
@ViewBuilder
private var inviteButton: some View {
if context.viewState.canInviteUsers, ServiceLocator.shared.settings.inviteMorePeopleFlowEnabled {
Button {
context.send(viewAction: .invite)
} label: {
Text(L10n.actionInvite)
}
}
}
}
// MARK: - Previews