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:
Stefan Ceriu
2023-01-17 09:31:53 +02:00
committed by GitHub
parent 1b9d1fedf1
commit 4b56e4af73
15 changed files with 113 additions and 132 deletions
@@ -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))
}
}
@@ -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)
}
@@ -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)")
@@ -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 }
}
@@ -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)
}
}
}
@@ -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)
}