mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Room member details cleanup (#452)
* Fixes #421 - Stop loading room members twice for displaying the members screen * Rename RoomMembers to RoomMemberDetails throughout * Add changelog
This commit is contained in:
@@ -45,10 +45,9 @@ final class RoomDetailsCoordinator: CoordinatorProtocol {
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.debug("RoomDetailsViewModel did complete with result: \(action).")
|
||||
switch action {
|
||||
case .peopleTapped:
|
||||
self.showPeople()
|
||||
case .requestMemberDetailsPresentation(let members):
|
||||
self.presentRoomMemberDetails(members)
|
||||
case .cancel:
|
||||
self.callback?(.cancel)
|
||||
}
|
||||
@@ -58,15 +57,15 @@ final class RoomDetailsCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(RoomDetailsScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
private func showPeople() {
|
||||
let params = RoomMembersCoordinatorParameters(roomProxy: parameters.roomProxy,
|
||||
mediaProvider: parameters.mediaProvider)
|
||||
let coordinator = RoomMembersCoordinator(parameters: params)
|
||||
|
||||
private func presentRoomMemberDetails(_ members: [RoomMemberProxy]) {
|
||||
let params = RoomMemberDetailsCoordinatorParameters(mediaProvider: parameters.mediaProvider,
|
||||
members: members)
|
||||
let coordinator = RoomMemberDetailsCoordinator(parameters: params)
|
||||
coordinator.callback = { [weak self] _ in
|
||||
self?.navigationStackCoordinator.pop()
|
||||
}
|
||||
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import UIKit
|
||||
// MARK: View model
|
||||
|
||||
enum RoomDetailsViewModelAction {
|
||||
case peopleTapped
|
||||
case requestMemberDetailsPresentation([RoomMemberProxy])
|
||||
case cancel
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,17 @@ import SwiftUI
|
||||
typealias RoomDetailsViewModelType = StateStoreViewModel<RoomDetailsViewState, RoomDetailsViewAction>
|
||||
|
||||
class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtocol {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let roomProxy: RoomProxyProtocol
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
private var members: [RoomMemberProxy] = [] {
|
||||
didSet {
|
||||
state.members = members.map { RoomDetailsMember(withProxy: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
var callback: ((RoomDetailsViewModelAction) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol) {
|
||||
self.roomProxy = roomProxy
|
||||
@@ -47,7 +45,7 @@ class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtoc
|
||||
Task {
|
||||
switch await roomProxy.members() {
|
||||
case .success(let members):
|
||||
state.members = members.map { RoomDetailsMember(withProxy: $0) }
|
||||
self.members = members
|
||||
case .failure(let error):
|
||||
MXLog.debug("Failed to retrieve room members: \(error)")
|
||||
state.bindings.alertInfo = AlertInfo(id: .alert(ElementL10n.unknownError))
|
||||
@@ -69,7 +67,7 @@ class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtoc
|
||||
override func process(viewAction: RoomDetailsViewAction) async {
|
||||
switch viewAction {
|
||||
case .processTapPeople:
|
||||
callback?(.peopleTapped)
|
||||
callback?(.requestMemberDetailsPresentation(members))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-14
@@ -16,32 +16,29 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RoomMembersCoordinatorParameters {
|
||||
let roomProxy: RoomProxyProtocol
|
||||
struct RoomMemberDetailsCoordinatorParameters {
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let members: [RoomMemberProxy]
|
||||
}
|
||||
|
||||
enum RoomMembersCoordinatorAction {
|
||||
enum RoomMemberDetailsCoordinatorAction {
|
||||
case cancel
|
||||
}
|
||||
|
||||
final class RoomMembersCoordinator: CoordinatorProtocol {
|
||||
private let parameters: RoomMembersCoordinatorParameters
|
||||
private var viewModel: RoomMembersViewModelProtocol
|
||||
final class RoomMemberDetailsCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: RoomMemberDetailsViewModelProtocol
|
||||
|
||||
var callback: ((RoomMembersCoordinatorAction) -> Void)?
|
||||
var callback: ((RoomMemberDetailsCoordinatorAction) -> Void)?
|
||||
|
||||
init(parameters: RoomMembersCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = RoomMembersViewModel(roomProxy: parameters.roomProxy,
|
||||
mediaProvider: parameters.mediaProvider)
|
||||
init(parameters: RoomMemberDetailsCoordinatorParameters) {
|
||||
viewModel = RoomMemberDetailsViewModel(mediaProvider: parameters.mediaProvider,
|
||||
members: parameters.members)
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.debug("RoomMembersViewModel did complete with result: \(action).")
|
||||
MXLog.debug("RoomMemberDetailsViewModel did complete with result: \(action).")
|
||||
switch action {
|
||||
case .cancel:
|
||||
self.callback?(.cancel)
|
||||
@@ -50,6 +47,6 @@ final class RoomMembersCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(RoomMembersScreen(context: viewModel.context))
|
||||
AnyView(RoomMemberDetailsScreen(context: viewModel.context))
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -16,14 +16,14 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum RoomMembersViewModelAction {
|
||||
enum RoomMemberDetailsViewModelAction {
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct RoomMembersViewState: BindableState {
|
||||
struct RoomMemberDetailsViewState: BindableState {
|
||||
var members: [RoomDetailsMember]
|
||||
|
||||
var bindings: RoomMembersViewStateBindings
|
||||
var bindings: RoomMemberDetailsViewStateBindings
|
||||
|
||||
var visibleMembers: [RoomDetailsMember] {
|
||||
if bindings.searchQuery.isEmpty {
|
||||
@@ -37,14 +37,14 @@ struct RoomMembersViewState: BindableState {
|
||||
}
|
||||
}
|
||||
|
||||
struct RoomMembersViewStateBindings {
|
||||
struct RoomMemberDetailsViewStateBindings {
|
||||
var searchQuery = ""
|
||||
|
||||
/// Information describing the currently displayed alert.
|
||||
var alertInfo: AlertInfo<RoomDetailsErrorType>?
|
||||
}
|
||||
|
||||
enum RoomMembersViewAction {
|
||||
enum RoomMemberDetailsViewAction {
|
||||
case selectMember(id: String)
|
||||
case loadMemberData(id: String)
|
||||
}
|
||||
+8
-20
@@ -16,35 +16,23 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
typealias RoomMembersViewModelType = StateStoreViewModel<RoomMembersViewState, RoomMembersViewAction>
|
||||
typealias RoomMemberDetailsViewModelType = StateStoreViewModel<RoomMemberDetailsViewState, RoomMemberDetailsViewAction>
|
||||
|
||||
class RoomMembersViewModel: RoomMembersViewModelType, RoomMembersViewModelProtocol {
|
||||
private let roomProxy: RoomProxyProtocol
|
||||
class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDetailsViewModelProtocol {
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
|
||||
var callback: ((RoomMemberDetailsViewModelAction) -> Void)?
|
||||
|
||||
var callback: ((RoomMembersViewModelAction) -> Void)?
|
||||
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol) {
|
||||
self.roomProxy = roomProxy
|
||||
init(mediaProvider: MediaProviderProtocol,
|
||||
members: [RoomMemberProxy]) {
|
||||
self.mediaProvider = mediaProvider
|
||||
super.init(initialViewState: .init(members: [],
|
||||
super.init(initialViewState: .init(members: members.map { RoomDetailsMember(withProxy: $0) },
|
||||
bindings: .init()))
|
||||
|
||||
Task {
|
||||
switch await roomProxy.members() {
|
||||
case .success(let members):
|
||||
state.members = members.map { RoomDetailsMember(withProxy: $0) }
|
||||
case .failure(let error):
|
||||
MXLog.debug("Failed to retrieve room members: \(error)")
|
||||
state.bindings.alertInfo = AlertInfo(id: .alert(ElementL10n.unknownError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: RoomMembersViewAction) async {
|
||||
override func process(viewAction: RoomMemberDetailsViewAction) async {
|
||||
switch viewAction {
|
||||
case .selectMember(let id):
|
||||
MXLog.debug("Member selected: \(id)")
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
protocol RoomMembersViewModelProtocol {
|
||||
var callback: ((RoomMembersViewModelAction) -> Void)? { get set }
|
||||
var context: RoomMembersViewModelType.Context { get }
|
||||
protocol RoomMemberDetailsViewModelProtocol {
|
||||
var callback: ((RoomMemberDetailsViewModelAction) -> Void)? { get set }
|
||||
var context: RoomMemberDetailsViewModelType.Context { get }
|
||||
}
|
||||
+7
-9
@@ -16,11 +16,11 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RoomMembersMemberCell: View {
|
||||
struct RoomMemberDetailsMemberCell: View {
|
||||
@ScaledMetric private var avatarSize = AvatarSize.user(on: .roomDetails).value
|
||||
|
||||
let member: RoomDetailsMember
|
||||
let context: RoomMembersViewModel.Context
|
||||
let context: RoomMemberDetailsViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
@@ -56,7 +56,7 @@ struct RoomMembersMemberCell: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct RoomMembersMemberCell_Previews: PreviewProvider {
|
||||
struct RoomMemberDetailsMemberCell_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
body.preferredColorScheme(.light)
|
||||
.tint(.element.accent)
|
||||
@@ -70,14 +70,12 @@ struct RoomMembersMemberCell_Previews: PreviewProvider {
|
||||
.mockBob,
|
||||
.mockCharlie
|
||||
]
|
||||
let roomProxy = MockRoomProxy(displayName: "Room A",
|
||||
members: members)
|
||||
let viewModel = RoomMembersViewModel(roomProxy: roomProxy,
|
||||
mediaProvider: MockMediaProvider())
|
||||
|
||||
let viewModel = RoomMemberDetailsViewModel(mediaProvider: MockMediaProvider(),
|
||||
members: members)
|
||||
|
||||
return VStack {
|
||||
ForEach(members) { member in
|
||||
RoomMembersMemberCell(member: .init(withProxy: member), context: viewModel.context)
|
||||
RoomMemberDetailsMemberCell(member: .init(withProxy: member), context: viewModel.context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-17
@@ -16,22 +16,25 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RoomMembersScreen: View {
|
||||
struct RoomMemberDetailsScreen: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
@ObservedObject var context: RoomMembersViewModel.Context
|
||||
@ObservedObject var context: RoomMemberDetailsViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
ForEach(context.viewState.visibleMembers) { member in
|
||||
RoomMembersMemberCell(member: member, context: context)
|
||||
.id(member.id)
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading) {
|
||||
Section {
|
||||
ForEach(context.viewState.visibleMembers) { member in
|
||||
RoomMemberDetailsMemberCell(member: member, context: context)
|
||||
.id(member.id)
|
||||
}
|
||||
} footer: {
|
||||
Text(ElementL10n.roomTitleMembers(context.viewState.members.count))
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
.font(.element.footnote)
|
||||
}
|
||||
} footer: {
|
||||
Text(ElementL10n.roomTitleMembers(context.viewState.members.count))
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
.font(.element.footnote)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
.searchable(text: $context.searchQuery, placement: .navigationBarDrawer(displayMode: .always))
|
||||
@@ -42,7 +45,7 @@ struct RoomMembersScreen: View {
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RoomMembers_Previews: PreviewProvider {
|
||||
struct RoomMemberDetails_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
let members: [RoomMemberProxy] = [
|
||||
@@ -50,11 +53,9 @@ struct RoomMembers_Previews: PreviewProvider {
|
||||
.mockBob,
|
||||
.mockCharlie
|
||||
]
|
||||
let roomProxy = MockRoomProxy(displayName: "Room A",
|
||||
members: members)
|
||||
let viewModel = RoomMembersViewModel(roomProxy: roomProxy,
|
||||
mediaProvider: MockMediaProvider())
|
||||
RoomMembersScreen(context: viewModel.context)
|
||||
let viewModel = RoomMemberDetailsViewModel(mediaProvider: MockMediaProvider(),
|
||||
members: members)
|
||||
RoomMemberDetailsScreen(context: viewModel.context)
|
||||
}
|
||||
.tint(.element.accent)
|
||||
}
|
||||
Reference in New Issue
Block a user