Skip to content

Commit b075ddb

Browse files
authored
Fixes #2414 - Move member loading to the room member detail screen, avoid blocking the whole application
1 parent f9edb75 commit b075ddb

31 files changed

+305
-305
lines changed

ElementX/Sources/Application/Application.swift

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct Application: App {
3131
} else {
3232
appCoordinator = AppCoordinator(appDelegate: appDelegate)
3333
}
34+
3435
SceneDelegate.windowManager = appCoordinator.windowManager
3536
}
3637

ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift

+19-28
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
109109
case .roomList:
110110
stateMachine.tryEvent(.dismissRoom, userInfo: EventUserInfo(animated: animated))
111111
case .roomMemberDetails(let userID):
112-
Task {
113-
switch await roomProxy?.getMember(userID: userID) {
114-
case .success(let member):
115-
stateMachine.tryEvent(.presentRoomMemberDetails(member: .init(value: member)))
116-
case .failure(let error):
117-
MXLog.error("[RoomFlowCoordinator] Failed to get member: \(error)")
118-
case .none:
119-
MXLog.error("[RoomFlowCoordinator] Failed to get member: RoomProxy is nil")
120-
}
121-
}
112+
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID))
122113
case .genericCallLink, .oidcCallback, .settings, .chatBackupSettings:
123114
break
124115
}
@@ -173,10 +164,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
173164
case (.roomMembersList(let roomID), .dismissRoomMembersList):
174165
return .roomDetails(roomID: roomID, isRoot: false)
175166

176-
case (.room(let roomID), .presentRoomMemberDetails(let member)):
177-
return .roomMemberDetails(roomID: roomID, member: member, fromRoomMembersList: false)
178-
case (.roomMembersList(let roomID), .presentRoomMemberDetails(let member)):
179-
return .roomMemberDetails(roomID: roomID, member: member, fromRoomMembersList: true)
167+
case (.room(let roomID), .presentRoomMemberDetails(userID: let userID)):
168+
return .roomMemberDetails(roomID: roomID, userID: userID, fromRoomMembersList: false)
169+
case (.roomMembersList(let roomID), .presentRoomMemberDetails(userID: let userID)):
170+
return .roomMemberDetails(roomID: roomID, userID: userID, fromRoomMembersList: true)
180171
case (.roomMemberDetails(let roomID, _, let fromRoomMembersList), .dismissRoomMemberDetails):
181172
return fromRoomMembersList ? .roomMembersList(roomID: roomID) : .room(roomID: roomID)
182173

@@ -285,13 +276,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
285276
case (.roomMembersList, .dismissRoomMembersList, .roomDetails):
286277
break
287278

288-
case (.room, .presentRoomMemberDetails, .roomMemberDetails(_, let member, _)):
289-
presentRoomMemberDetails(member: member.value)
279+
case (.room, .presentRoomMemberDetails, .roomMemberDetails(_, let userID, _)):
280+
presentRoomMemberDetails(userID: userID)
290281
case (.roomMemberDetails, .dismissRoomMemberDetails, .room):
291282
break
292283

293-
case (.roomMembersList, .presentRoomMemberDetails, .roomMemberDetails(_, let member, _)):
294-
presentRoomMemberDetails(member: member.value)
284+
case (.roomMembersList, .presentRoomMemberDetails, .roomMemberDetails(_, let userID, _)):
285+
presentRoomMemberDetails(userID: userID)
295286
case (.roomMemberDetails, .dismissRoomMemberDetails, .roomMembersList):
296287
break
297288

@@ -469,8 +460,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
469460
stateMachine.tryEvent(.presentPollForm(mode: mode))
470461
case .presentLocationViewer(_, let geoURI, let description):
471462
stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description)))
472-
case .presentRoomMemberDetails(member: let member):
473-
stateMachine.tryEvent(.presentRoomMemberDetails(member: .init(value: member)))
463+
case .presentRoomMemberDetails(userID: let userID):
464+
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID))
474465
case .presentMessageForwarding(let itemID):
475466
stateMachine.tryEvent(.presentMessageForwarding(itemID: itemID))
476467
case .presentCallScreen:
@@ -598,7 +589,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
598589
case .invite:
599590
stateMachine.tryEvent(.presentInviteUsersScreen)
600591
case .selectedMember(let member):
601-
stateMachine.tryEvent(.presentRoomMemberDetails(member: .init(value: member)))
592+
stateMachine.tryEvent(.presentRoomMemberDetails(userID: member.userID))
602593
}
603594
}
604595
.store(in: &cancellables)
@@ -931,21 +922,21 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
931922
}
932923
}
933924

934-
private func presentRoomMemberDetails(member: RoomMemberProxyProtocol) {
925+
private func presentRoomMemberDetails(userID: String) {
935926
guard let roomProxy else {
936927
fatalError()
937928
}
938929

939930
let params = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: roomProxy,
940-
roomMemberProxy: member,
931+
userID: userID,
941932
mediaProvider: userSession.mediaProvider,
942933
userIndicatorController: userIndicatorController)
943934
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params)
944935

945936
coordinator.actions.sink { [weak self] action in
946937
guard let self else { return }
947938
switch action {
948-
case .openDirectChat:
939+
case .openDirectChat(let displayName):
949940
let loadingIndicatorIdentifier = "OpenDirectChatLoadingIndicator"
950941

951942
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier,
@@ -956,12 +947,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
956947
Task { [weak self] in
957948
guard let self else { return }
958949

959-
let currentDirectRoom = await userSession.clientProxy.directRoomForUserID(member.userID)
950+
let currentDirectRoom = await userSession.clientProxy.directRoomForUserID(userID)
960951
switch currentDirectRoom {
961952
case .success(.some(let roomID)):
962953
stateMachine.tryEvent(.presentRoom(roomID: roomID))
963954
case .success(nil):
964-
switch await userSession.clientProxy.createDirectRoom(with: member.userID, expectedRoomName: member.displayName) {
955+
switch await userSession.clientProxy.createDirectRoom(with: userID, expectedRoomName: displayName) {
965956
case .success(let roomID):
966957
analytics.trackCreatedRoom(isDM: true)
967958
stateMachine.tryEvent(.presentRoom(roomID: roomID))
@@ -1185,7 +1176,7 @@ private extension RoomFlowCoordinator {
11851176
case notificationSettings(roomID: String)
11861177
case globalNotificationSettings(roomID: String)
11871178
case roomMembersList(roomID: String)
1188-
case roomMemberDetails(roomID: String, member: HashableRoomMemberWrapper, fromRoomMembersList: Bool)
1179+
case roomMemberDetails(roomID: String, userID: String, fromRoomMembersList: Bool)
11891180
case inviteUsersScreen(roomID: String, fromRoomMembersList: Bool)
11901181
case mediaUploadPicker(roomID: String, source: MediaPickerScreenSource)
11911182
case mediaUploadPreview(roomID: String, fileURL: URL)
@@ -1225,7 +1216,7 @@ private extension RoomFlowCoordinator {
12251216
case presentRoomMembersList
12261217
case dismissRoomMembersList
12271218

1228-
case presentRoomMemberDetails(member: HashableRoomMemberWrapper)
1219+
case presentRoomMemberDetails(userID: String)
12291220
case dismissRoomMemberDetails
12301221

12311222
case presentInviteUsersScreen

ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenCoordinator.swift

+8-6
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import SwiftUI
1919

2020
struct RoomMemberDetailsScreenCoordinatorParameters {
2121
let roomProxy: RoomProxyProtocol
22-
let roomMemberProxy: RoomMemberProxyProtocol
22+
let userID: String
2323
let mediaProvider: MediaProviderProtocol
2424
let userIndicatorController: UserIndicatorControllerProtocol
2525
}
2626

2727
enum RoomMemberDetailsScreenCoordinatorAction {
28-
case openDirectChat
28+
case openDirectChat(displayName: String?)
2929
}
3030

3131
final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
@@ -40,7 +40,7 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
4040

4141
init(parameters: RoomMemberDetailsScreenCoordinatorParameters) {
4242
viewModel = RoomMemberDetailsScreenViewModel(roomProxy: parameters.roomProxy,
43-
roomMemberProxy: parameters.roomMemberProxy,
43+
userID: parameters.userID,
4444
mediaProvider: parameters.mediaProvider,
4545
userIndicatorController: parameters.userIndicatorController)
4646
}
@@ -50,14 +50,16 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
5050
guard let self else { return }
5151

5252
switch action {
53-
case .openDirectChat:
54-
actionsSubject.send(.openDirectChat)
53+
case .openDirectChat(let displayName):
54+
actionsSubject.send(.openDirectChat(displayName: displayName))
5555
}
5656
}
5757
.store(in: &cancellables)
5858
}
5959

60-
func stop() { viewModel.stop() }
60+
func stop() {
61+
viewModel.stop()
62+
}
6163

6264
func toPresentable() -> AnyView {
6365
AnyView(RoomMemberDetailsScreen(context: viewModel.context))

ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
import Foundation
1818

1919
enum RoomMemberDetailsScreenViewModelAction {
20-
case openDirectChat
20+
case openDirectChat(displayName: String?)
2121
}
2222

2323
struct RoomMemberDetailsScreenViewState: BindableState {
24-
var details: RoomMemberDetails
24+
let userID: String
25+
var memberDetails: RoomMemberDetails?
2526
var isProcessingIgnoreRequest = false
2627

2728
var bindings: RoomMemberDetailsScreenViewStateBindings

ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift

+59-8
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,55 @@ typealias RoomMemberDetailsScreenViewModelType = StateStoreViewModel<RoomMemberD
2121

2222
class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, RoomMemberDetailsScreenViewModelProtocol {
2323
private let roomProxy: RoomProxyProtocol
24-
private let roomMemberProxy: RoomMemberProxyProtocol
24+
private let userID: String
2525
private let mediaProvider: MediaProviderProtocol
2626
private let userIndicatorController: UserIndicatorControllerProtocol
2727

2828
private var actionsSubject: PassthroughSubject<RoomMemberDetailsScreenViewModelAction, Never> = .init()
2929

30+
private var roomMemberProxy: RoomMemberProxyProtocol?
31+
3032
var actions: AnyPublisher<RoomMemberDetailsScreenViewModelAction, Never> {
3133
actionsSubject.eraseToAnyPublisher()
3234
}
3335

3436
init(roomProxy: RoomProxyProtocol,
35-
roomMemberProxy: RoomMemberProxyProtocol,
37+
userID: String,
3638
mediaProvider: MediaProviderProtocol,
3739
userIndicatorController: UserIndicatorControllerProtocol) {
3840
self.roomProxy = roomProxy
39-
self.roomMemberProxy = roomMemberProxy
41+
self.userID = userID
4042
self.mediaProvider = mediaProvider
4143
self.userIndicatorController = userIndicatorController
4244

43-
let initialViewState = RoomMemberDetailsScreenViewState(details: RoomMemberDetails(withProxy: roomMemberProxy),
44-
bindings: .init())
45+
let initialViewState = RoomMemberDetailsScreenViewState(userID: userID, bindings: .init())
4546

4647
super.init(initialViewState: initialViewState, imageProvider: mediaProvider)
48+
49+
showMemberLoadingIndicator()
50+
Task {
51+
defer {
52+
hideMemberLoadingIndicator()
53+
}
54+
55+
switch await roomProxy.getMember(userID: userID) {
56+
case .success(let member):
57+
roomMemberProxy = member
58+
state.memberDetails = RoomMemberDetails(withProxy: member)
59+
case .failure(let error):
60+
state.bindings.alertInfo = .init(id: .unknown)
61+
MXLog.error("[RoomFlowCoordinator] Failed to get member: \(error)")
62+
}
63+
}
4764
}
4865

4966
// MARK: - Public
5067

5168
func stop() {
5269
// Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier.
5370
state.bindings.mediaPreviewItem = nil
71+
72+
hideMemberLoadingIndicator()
5473
}
5574

5675
override func process(viewAction: RoomMemberDetailsScreenViewAction) {
@@ -66,20 +85,28 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
6685
case .displayAvatar:
6786
displayFullScreenAvatar()
6887
case .openDirectChat:
69-
actionsSubject.send(.openDirectChat)
88+
guard let roomMemberProxy else {
89+
fatalError()
90+
}
91+
92+
actionsSubject.send(.openDirectChat(displayName: roomMemberProxy.displayName))
7093
}
7194
}
7295

7396
// MARK: - Private
7497

7598
@MainActor
7699
private func ignoreUser() async {
100+
guard let roomMemberProxy else {
101+
fatalError()
102+
}
103+
77104
state.isProcessingIgnoreRequest = true
78105
let result = await roomMemberProxy.ignoreUser()
79106
state.isProcessingIgnoreRequest = false
80107
switch result {
81108
case .success:
82-
state.details.isIgnored = true
109+
state.memberDetails?.isIgnored = true
83110
updateMembers()
84111
case .failure:
85112
state.bindings.alertInfo = .init(id: .unknown)
@@ -88,12 +115,16 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
88115

89116
@MainActor
90117
private func unignoreUser() async {
118+
guard let roomMemberProxy else {
119+
fatalError()
120+
}
121+
91122
state.isProcessingIgnoreRequest = true
92123
let result = await roomMemberProxy.unignoreUser()
93124
state.isProcessingIgnoreRequest = false
94125
switch result {
95126
case .success:
96-
state.details.isIgnored = false
127+
state.memberDetails?.isIgnored = false
97128
updateMembers()
98129
case .failure:
99130
state.bindings.alertInfo = .init(id: .unknown)
@@ -107,6 +138,10 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
107138
}
108139

109140
private func displayFullScreenAvatar() {
141+
guard let roomMemberProxy else {
142+
fatalError()
143+
}
144+
110145
guard let avatarURL = roomMemberProxy.avatarURL else {
111146
return
112147
}
@@ -125,4 +160,20 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
125160
}
126161
}
127162
}
163+
164+
// MARK: Loading indicator
165+
166+
private static let loadingIndicatorIdentifier = "RoomMemberLoading"
167+
168+
private func showMemberLoadingIndicator() {
169+
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
170+
type: .modal(progress: .indeterminate, interactiveDismissDisabled: false, allowsInteraction: true),
171+
title: L10n.commonLoading,
172+
persistent: true),
173+
delay: .milliseconds(100))
174+
}
175+
176+
private func hideMemberLoadingIndicator() {
177+
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
178+
}
128179
}

ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModelProtocol.swift

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ import Combine
2020
protocol RoomMemberDetailsScreenViewModelProtocol {
2121
var actions: AnyPublisher<RoomMemberDetailsScreenViewModelAction, Never> { get }
2222
var context: RoomMemberDetailsScreenViewModelType.Context { get }
23+
2324
func stop()
2425
}

0 commit comments

Comments
 (0)