mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 15:22:39 +00:00
Join room loading indicators
This commit is contained in:
parent
4e67b0b406
commit
92b31a0f01
20 changed files with 287 additions and 265 deletions
|
@ -68,7 +68,8 @@ public class UserIndicator {
|
|||
|
||||
/// Cancel the indicator, triggering any dismissal action / animation
|
||||
///
|
||||
/// Note: clients can call this method directly, if they have access to the `UserIndicator`.
|
||||
/// Note: clients can call this method directly, if they have access to the `UserIndicator`. Alternatively
|
||||
/// deallocating the `UserIndicator` will call `cancel` automatically.
|
||||
/// Once cancelled, `UserIndicatorQueue` will automatically start the next `UserIndicator` in the queue.
|
||||
public func cancel() {
|
||||
complete()
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"send_to" = "Send to %@";
|
||||
"close" = "Close";
|
||||
"skip" = "Skip";
|
||||
"joining" = "Joining";
|
||||
"joined" = "Joined";
|
||||
"switch" = "Switch";
|
||||
"more" = "More";
|
||||
|
|
|
@ -2219,6 +2219,10 @@ public class VectorL10n: NSObject {
|
|||
public static var joined: String {
|
||||
return VectorL10n.tr("Vector", "joined")
|
||||
}
|
||||
/// Joining
|
||||
public static var joining: String {
|
||||
return VectorL10n.tr("Vector", "joining")
|
||||
}
|
||||
/// Done
|
||||
public static var keyBackupRecoverDoneAction: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_recover_done_action")
|
||||
|
|
27
Riot/Modules/Common/UserIndicators/UserIndicatorCancel.h
Normal file
27
Riot/Modules/Common/UserIndicators/UserIndicatorCancel.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef UserIndicatorCancel_h
|
||||
#define UserIndicatorCancel_h
|
||||
|
||||
/**
|
||||
Callback function to cancel a `UserIndicator` without needing a direct reference to the object
|
||||
|
||||
Note: the function is defined in Objective-C (instead of Swift) to be accessible by both languages.
|
||||
*/
|
||||
typedef void (^UserIndicatorCancel)(void);
|
||||
|
||||
#endif /* UserIndicatorCancel_h */
|
54
Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift
Normal file
54
Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CommonKit
|
||||
|
||||
/// An abstraction on top of `UserIndicatorTypePresenterProtocol` which manages and stores the individual user indicators.
|
||||
/// When used to present an indicator the `UserIndicatorStore` will instead returns a simple callback function to the clients
|
||||
/// letting them cancel the indicators without worrying about memory.
|
||||
@objc final class UserIndicatorStore: NSObject {
|
||||
private let presenter: UserIndicatorTypePresenterProtocol
|
||||
private var indicators: [UserIndicator]
|
||||
|
||||
init(presenter: UserIndicatorTypePresenterProtocol) {
|
||||
self.presenter = presenter
|
||||
self.indicators = []
|
||||
}
|
||||
|
||||
/// Present a new type of user indicator, such as loading spinner or success message.
|
||||
/// To remove an indicator, call the returned `UserIndicatorCancel` function
|
||||
func present(type: UserIndicatorType) -> UserIndicatorCancel {
|
||||
let indicator = presenter.present(type)
|
||||
indicators.append(indicator)
|
||||
return {
|
||||
indicator.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
/// Present a new type of user indicator, such as loading spinner or success message.
|
||||
/// To remove an indicator, call the returned `UserIndicatorCancel` function
|
||||
///
|
||||
/// Note: This is a convenience function callable by objective-c code
|
||||
@objc func presentLoading(label: String, isInteractionBlocking: Bool) -> UserIndicatorCancel {
|
||||
present(
|
||||
type: .loading(
|
||||
label: label,
|
||||
isInteractionBlocking: isInteractionBlocking
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -15,3 +15,4 @@
|
|||
#import "MXKRoomInputToolbarView.h"
|
||||
#import "MXKImageView.h"
|
||||
#import "MXKRoomBubbleCellData.h"
|
||||
#import "UserIndicatorCancel.h"
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#import "MXKAttachmentsViewController.h"
|
||||
#import "MXKAttachmentAnimator.h"
|
||||
|
||||
@class UserIndicatorStore;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
|
||||
MXKRoomViewControllerJoinRoomResultSuccess,
|
||||
MXKRoomViewControllerJoinRoomResultFailureRoomEmpty,
|
||||
|
@ -198,6 +200,12 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
|
|||
*/
|
||||
@property NSTimeInterval resizeComposerAnimationDuration;
|
||||
|
||||
/**
|
||||
A store of user indicators that lets the room present and dismiss indicators without
|
||||
worrying about the presentation context or memory management.
|
||||
*/
|
||||
@property (strong, nonatomic) UserIndicatorStore *userIndicatorStore;
|
||||
|
||||
/**
|
||||
This object is defined when the displayed room is left. It is added into the bubbles table header.
|
||||
This label is used to display the reason why the room has been left.
|
||||
|
|
|
@ -859,12 +859,11 @@
|
|||
return;
|
||||
}
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
UserIndicatorCancel cancelIndicator = [self.userIndicatorStore presentLoadingWithLabel:[VectorL10n joining] isInteractionBlocking:YES];
|
||||
joinRoomRequest = [roomDataSource.room join:^{
|
||||
|
||||
self->joinRoomRequest = nil;
|
||||
[self stopActivityIndicator];
|
||||
cancelIndicator();
|
||||
|
||||
[self triggerInitialBackPagination];
|
||||
|
||||
|
@ -874,6 +873,7 @@
|
|||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
cancelIndicator();
|
||||
MXLogDebug(@"[MXKRoomVC] Failed to join room (%@)", self->roomDataSource.room.summary.displayname);
|
||||
[self processRoomJoinFailureWithError:error completion:completion];
|
||||
}];
|
||||
|
@ -894,12 +894,11 @@
|
|||
return;
|
||||
}
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
UserIndicatorCancel cancelIndicator = [self.userIndicatorStore presentLoadingWithLabel:[VectorL10n joining] isInteractionBlocking:YES];
|
||||
void (^success)(MXRoom *room) = ^(MXRoom *room) {
|
||||
|
||||
self->joinRoomRequest = nil;
|
||||
[self stopActivityIndicator];
|
||||
cancelIndicator();
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
|
@ -921,6 +920,7 @@
|
|||
};
|
||||
|
||||
void (^failure)(NSError *error) = ^(NSError *error) {
|
||||
cancelIndicator();
|
||||
MXLogDebug(@"[MXKRoomVC] Failed to join room (%@)", roomIdOrAlias);
|
||||
[self processRoomJoinFailureWithError:error completion:completion];
|
||||
};
|
||||
|
|
|
@ -28,9 +28,9 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
|
|||
|
||||
private let parameters: RoomCoordinatorParameters
|
||||
private let roomViewController: RoomViewController
|
||||
private let activityIndicatorPresenter: ActivityIndicatorPresenterType
|
||||
private let userIndicatorStore: UserIndicatorStore
|
||||
private var selectedEventId: String?
|
||||
private var loadingIndicator: UserIndicator?
|
||||
private var loadingCancel: UserIndicatorCancel?
|
||||
|
||||
private var roomDataSourceManager: MXKRoomDataSourceManager {
|
||||
return MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.parameters.session)
|
||||
|
@ -73,15 +73,16 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
|
|||
init(parameters: RoomCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
self.selectedEventId = parameters.eventId
|
||||
|
||||
self.userIndicatorStore = UserIndicatorStore(presenter: parameters.userIndicatorPresenter)
|
||||
|
||||
if let threadId = parameters.threadId {
|
||||
self.roomViewController = ThreadViewController.instantiate(withThreadId: threadId,
|
||||
configuration: parameters.displayConfiguration)
|
||||
} else {
|
||||
self.roomViewController = RoomViewController.instantiate(with: parameters.displayConfiguration)
|
||||
}
|
||||
self.roomViewController.userIndicatorStore = userIndicatorStore
|
||||
self.roomViewController.showSettingsInitially = parameters.showSettingsInitially
|
||||
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
|
||||
|
||||
self.roomViewController.parentSpaceId = parameters.parentSpaceId
|
||||
|
||||
|
@ -319,20 +320,21 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
|
|||
}
|
||||
|
||||
private func startLoading() {
|
||||
if let presenter = parameters.userIndicatorPresenter {
|
||||
if loadingIndicator == nil {
|
||||
MXLog.debug("[RoomCoordinator] Present loading indicator in a room: \(roomId ?? "unknown")")
|
||||
loadingIndicator = presenter.present(.loading(label: VectorL10n.homeSyncing, isInteractionBlocking: false))
|
||||
}
|
||||
} else {
|
||||
activityIndicatorPresenter.presentActivityIndicator(on: roomViewController.view, animated: true)
|
||||
// The `RoomViewController` does not currently ensure that `startLoading` is matched by corresponding `stopLoading` and may
|
||||
// thus trigger start of loading multiple times. To solve for this we will hold onto the cancellation reference of the
|
||||
// last loading request, and if one already exists, we will not present a new indicator.
|
||||
guard loadingCancel == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.debug("[RoomCoordinator] Present loading indicator in a room: \(roomId ?? "unknown")")
|
||||
loadingCancel = userIndicatorStore.present(type: .loading(label: VectorL10n.homeSyncing, isInteractionBlocking: false))
|
||||
}
|
||||
|
||||
private func stopLoading() {
|
||||
MXLog.debug("[RoomCoordinator] Dismiss loading indicator in a room: \(roomId ?? "unknown")")
|
||||
loadingIndicator = nil
|
||||
activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
loadingCancel?()
|
||||
loadingCancel = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,10 +450,6 @@ extension RoomCoordinator: RoomViewControllerDelegate {
|
|||
startEditPollCoordinator(startEvent: startEvent)
|
||||
}
|
||||
|
||||
func roomViewControllerCanDelegateUserIndicators(_ roomViewController: RoomViewController) -> Bool {
|
||||
return parameters.userIndicatorPresenter != nil
|
||||
}
|
||||
|
||||
func roomViewControllerDidStartLoading(_ roomViewController: RoomViewController) {
|
||||
startLoading()
|
||||
}
|
||||
|
@ -467,4 +465,18 @@ extension RoomCoordinator: RoomViewControllerDelegate {
|
|||
func roomViewControllerDidStopLiveLocationSharing(_ roomViewController: RoomViewController) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
func threadsCoordinator(for roomViewController: RoomViewController, threadId: String?) -> ThreadsCoordinatorBridgePresenter? {
|
||||
guard let session = mxSession, let roomId = roomId else {
|
||||
MXLog.error("[RoomCoordinator] Cannot create threads coordinator for room \(roomId ?? "")")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ThreadsCoordinatorBridgePresenter(
|
||||
session: session,
|
||||
roomId: roomId,
|
||||
threadId: threadId,
|
||||
userIndicatorPresenter: parameters.userIndicatorPresenter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
@objc protocol RoomCoordinatorBridgePresenterDelegate {
|
||||
func roomCoordinatorBridgePresenterDidLeaveRoom(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
func roomCoordinatorBridgePresenterDidCancelRoomPreview(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
func roomCoordinatorBridgePresenter(_ bridgePresenter: RoomCoordinatorBridgePresenter,
|
||||
didSelectRoomWithId roomId: String,
|
||||
eventId: String?)
|
||||
func roomCoordinatorBridgePresenter(_ bridgePresenter: RoomCoordinatorBridgePresenter, didReplaceRoomWithReplacementId roomId: String)
|
||||
func roomCoordinatorBridgePresenterDidDismissInteractively(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
class RoomCoordinatorBridgePresenterParameters: NSObject {
|
||||
|
||||
/// The matrix session in which the room should be available.
|
||||
let session: MXSession
|
||||
|
||||
/// The room identifier
|
||||
let roomId: String
|
||||
|
||||
/// The identifier of the parent space. `nil` for home space
|
||||
let parentSpaceId: String?
|
||||
|
||||
/// If not nil, the room will be opened on this event.
|
||||
let eventId: String?
|
||||
|
||||
/// If not nil, specified thread will be opened.
|
||||
let threadId: String?
|
||||
|
||||
/// Display configuration for the room
|
||||
let displayConfiguration: RoomDisplayConfiguration
|
||||
|
||||
/// The data for the room preview.
|
||||
let previewData: RoomPreviewData?
|
||||
|
||||
/// If `true`, the room settings screen will be initially displayed. Default `false`
|
||||
let showSettingsInitially: Bool
|
||||
|
||||
init(session: MXSession,
|
||||
roomId: String,
|
||||
parentSpaceId: String?,
|
||||
eventId: String?,
|
||||
threadId: String?,
|
||||
displayConfiguration: RoomDisplayConfiguration,
|
||||
previewData: RoomPreviewData?,
|
||||
showSettingsInitially: Bool) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.parentSpaceId = parentSpaceId
|
||||
self.eventId = eventId
|
||||
self.threadId = threadId
|
||||
self.displayConfiguration = displayConfiguration
|
||||
self.previewData = previewData
|
||||
self.showSettingsInitially = showSettingsInitially
|
||||
}
|
||||
}
|
||||
|
||||
/// RoomCoordinatorBridgePresenter enables to start RoomCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed
|
||||
/// once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class RoomCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let bridgeParameters: RoomCoordinatorBridgePresenterParameters
|
||||
private var coordinator: RoomCoordinator?
|
||||
private var navigationType: NavigationType = .present
|
||||
|
||||
private enum NavigationType {
|
||||
case present
|
||||
case push
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: RoomCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: RoomCoordinatorBridgePresenterParameters) {
|
||||
self.bridgeParameters = parameters
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
let coordinator = self.createRoomCoordinator(parentSpaceId: bridgeParameters.parentSpaceId)
|
||||
coordinator.delegate = self
|
||||
let presentable = coordinator.toPresentable()
|
||||
presentable.modalPresentationStyle = .formSheet
|
||||
viewController.present(presentable, animated: animated, completion: nil)
|
||||
coordinator.start()
|
||||
|
||||
self.coordinator = coordinator
|
||||
self.navigationType = .present
|
||||
}
|
||||
|
||||
func push(from navigationController: UINavigationController, animated: Bool) {
|
||||
|
||||
let navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController)
|
||||
|
||||
let coordinator = self.createRoomCoordinator(with: navigationRouter, parentSpaceId: bridgeParameters.parentSpaceId)
|
||||
coordinator.delegate = self
|
||||
coordinator.start() // Will trigger view controller push
|
||||
|
||||
self.coordinator = coordinator
|
||||
self.navigationType = .push
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
switch navigationType {
|
||||
case .present:
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
completion?()
|
||||
}
|
||||
case .push:
|
||||
guard let navigationController = coordinator.toPresentable().navigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.popViewController(animated: animated)
|
||||
self.coordinator = nil
|
||||
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func createRoomCoordinator(with navigationRouter: NavigationRouterType = NavigationRouter(navigationController: RiotNavigationController()), parentSpaceId: String?) -> RoomCoordinator {
|
||||
|
||||
let coordinatorParameters: RoomCoordinatorParameters
|
||||
|
||||
if let previewData = self.bridgeParameters.previewData {
|
||||
coordinatorParameters = RoomCoordinatorParameters(navigationRouter: navigationRouter, parentSpaceId: parentSpaceId, previewData: previewData)
|
||||
} else {
|
||||
coordinatorParameters = RoomCoordinatorParameters(navigationRouter: navigationRouter,
|
||||
session: self.bridgeParameters.session,
|
||||
parentSpaceId: parentSpaceId,
|
||||
roomId: self.bridgeParameters.roomId,
|
||||
eventId: self.bridgeParameters.eventId,
|
||||
threadId: self.bridgeParameters.threadId,
|
||||
showSettingsInitially: self.bridgeParameters.showSettingsInitially,
|
||||
displayConfiguration: self.bridgeParameters.displayConfiguration)
|
||||
}
|
||||
|
||||
return RoomCoordinator(parameters: coordinatorParameters)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomNotificationSettingsCoordinatorDelegate
|
||||
extension RoomCoordinatorBridgePresenter: RoomCoordinatorDelegate {
|
||||
|
||||
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didSelectRoomWithId roomId: String, eventId: String?) {
|
||||
self.delegate?.roomCoordinatorBridgePresenter(self, didSelectRoomWithId: roomId, eventId: eventId)
|
||||
}
|
||||
|
||||
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didReplaceRoomWithReplacementId roomId: String) {
|
||||
self.delegate?.roomCoordinatorBridgePresenter(self, didReplaceRoomWithReplacementId: roomId)
|
||||
}
|
||||
|
||||
func roomCoordinatorDidLeaveRoom(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidLeaveRoom(self)
|
||||
}
|
||||
|
||||
func roomCoordinatorDidCancelRoomPreview(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidCancelRoomPreview(self)
|
||||
}
|
||||
|
||||
func roomCoordinatorDidDismissInteractively(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidDismissInteractively(self)
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ struct RoomCoordinatorParameters {
|
|||
let navigationRouterStore: NavigationRouterStoreProtocol?
|
||||
|
||||
/// Presenter for displaying loading indicators, success messages and other user indicators
|
||||
let userIndicatorPresenter: UserIndicatorTypePresenterProtocol?
|
||||
let userIndicatorPresenter: UserIndicatorTypePresenterProtocol
|
||||
|
||||
/// The matrix session in which the room should be available.
|
||||
let session: MXSession
|
||||
|
@ -59,7 +59,7 @@ struct RoomCoordinatorParameters {
|
|||
|
||||
private init(navigationRouter: NavigationRouterType?,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol?,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol?,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol,
|
||||
session: MXSession,
|
||||
roomId: String,
|
||||
parentSpaceId: String?,
|
||||
|
@ -84,7 +84,7 @@ struct RoomCoordinatorParameters {
|
|||
/// Init to present a joined room
|
||||
init(navigationRouter: NavigationRouterType? = nil,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol? = nil,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol? = nil,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol,
|
||||
session: MXSession,
|
||||
parentSpaceId: String?,
|
||||
roomId: String,
|
||||
|
@ -109,12 +109,13 @@ struct RoomCoordinatorParameters {
|
|||
/// Init to present a room preview
|
||||
init(navigationRouter: NavigationRouterType? = nil,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol? = nil,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol,
|
||||
parentSpaceId: String?,
|
||||
previewData: RoomPreviewData) {
|
||||
|
||||
self.init(navigationRouter: navigationRouter,
|
||||
navigationRouterStore: navigationRouterStore,
|
||||
userIndicatorPresenter: nil,
|
||||
userIndicatorPresenter: userIndicatorPresenter,
|
||||
session: previewData.mxSession,
|
||||
roomId: previewData.roomId,
|
||||
parentSpaceId: parentSpaceId,
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
@class UniversalLinkParameters;
|
||||
@protocol RoomViewControllerDelegate;
|
||||
@class RoomDisplayConfiguration;
|
||||
@class ThreadsCoordinatorBridgePresenter;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -253,14 +254,6 @@ canEditPollWithEventIdentifier:(NSString *)eventIdentifier;
|
|||
- (void)roomViewController:(RoomViewController *)roomViewController
|
||||
didRequestEditForPollWithStartEvent:(MXEvent *)startEvent;
|
||||
|
||||
/**
|
||||
Checks whether the delegate supports handling of activity indicators
|
||||
|
||||
Note: This is a transition API whilst `RoomViewController` contains legacy activity indicators
|
||||
as well as using a newer user interaction presenters.
|
||||
*/
|
||||
- (BOOL)roomViewControllerCanDelegateUserIndicators:(RoomViewController *)roomViewController;
|
||||
|
||||
/**
|
||||
Indicate to the delegate that loading should start
|
||||
|
||||
|
@ -283,6 +276,9 @@ didRequestEditForPollWithStartEvent:(MXEvent *)startEvent;
|
|||
/// User tap live location sharing banner
|
||||
- (void)roomViewControllerDidTapLiveLocationSharingBanner:(RoomViewController *)roomViewController;
|
||||
|
||||
/// Request a threads coordinator for a given threadId, used to open a thread from within a room.
|
||||
- (nullable ThreadsCoordinatorBridgePresenter *)threadsCoordinatorForRoomViewController:(RoomViewController *)roomViewController threadId:(nullable NSString *)threadId;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -969,17 +969,13 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
#pragma mark - Loading indicators
|
||||
|
||||
- (BOOL)providesCustomActivityIndicator {
|
||||
return [self.delegate roomViewControllerCanDelegateUserIndicators:self];
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Override of a legacy method to determine whether to use a newer implementation instead.
|
||||
// Will be removed in the future https://github.com/vector-im/element-ios/issues/5608
|
||||
- (void)startActivityIndicator {
|
||||
if ([self providesCustomActivityIndicator]) {
|
||||
[self.delegate roomViewControllerDidStartLoading:self];
|
||||
} else {
|
||||
[super startActivityIndicator];
|
||||
}
|
||||
[self.delegate roomViewControllerDidStartLoading:self];
|
||||
}
|
||||
|
||||
// Override of a legacy method to determine whether to use a newer implementation instead.
|
||||
|
@ -992,15 +988,11 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
[MXSDKOptions.sharedInstance.profiler stopMeasuringTaskWithProfile:notificationTaskProfile];
|
||||
notificationTaskProfile = nil;
|
||||
}
|
||||
if ([self providesCustomActivityIndicator]) {
|
||||
// The legacy super implementation of `stopActivityIndicator` contains a number of checks grouped under `canStopActivityIndicator`
|
||||
// to determine whether the indicator can be stopped or not (and the method should thus rather be called `stopActivityIndicatorIfPossible`).
|
||||
// Since the newer indicators are not calling super implementation, the check for `canStopActivityIndicator` has to be performed manually.
|
||||
if ([self canStopActivityIndicator]) {
|
||||
[self stopLoadingUserIndicator];
|
||||
}
|
||||
} else {
|
||||
[super stopActivityIndicator];
|
||||
// The legacy super implementation of `stopActivityIndicator` contains a number of checks grouped under `canStopActivityIndicator`
|
||||
// to determine whether the indicator can be stopped or not (and the method should thus rather be called `stopActivityIndicatorIfPossible`).
|
||||
// Since the newer indicators are not calling super implementation, the check for `canStopActivityIndicator` has to be performed manually.
|
||||
if ([self canStopActivityIndicator]) {
|
||||
[self stopLoadingUserIndicator];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4733,9 +4725,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
|
||||
- (IBAction)onThreadListTapped:(id)sender
|
||||
{
|
||||
self.threadsBridgePresenter = [[ThreadsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession
|
||||
roomId:self.roomDataSource.roomId
|
||||
threadId:nil];
|
||||
self.threadsBridgePresenter = [self.delegate threadsCoordinatorForRoomViewController:self threadId:nil];
|
||||
self.threadsBridgePresenter.delegate = self;
|
||||
[self.threadsBridgePresenter pushFrom:self.navigationController animated:YES];
|
||||
|
||||
|
@ -6796,9 +6786,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
|||
self.threadsBridgePresenter = nil;
|
||||
}
|
||||
|
||||
self.threadsBridgePresenter = [[ThreadsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession
|
||||
roomId:self.roomDataSource.roomId
|
||||
threadId:threadId];
|
||||
self.threadsBridgePresenter = [self.delegate threadsCoordinatorForRoomViewController:self threadId:threadId];
|
||||
self.threadsBridgePresenter.delegate = self;
|
||||
[self.threadsBridgePresenter pushFrom:self.navigationController animated:YES];
|
||||
}
|
||||
|
|
|
@ -462,10 +462,6 @@ extension ExploreRoomCoordinator: RoomViewControllerDelegate {
|
|||
return TimelinePollProvider.shared.timelinePollCoordinatorForEventIdentifier(eventIdentifier)?.canEndPoll() ?? false
|
||||
}
|
||||
|
||||
func roomViewControllerCanDelegateUserIndicators(_ roomViewController: RoomViewController) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func roomViewControllerDidStartLoading(_ roomViewController: RoomViewController) {
|
||||
|
||||
}
|
||||
|
@ -481,6 +477,20 @@ extension ExploreRoomCoordinator: RoomViewControllerDelegate {
|
|||
func roomViewControllerDidStopLiveLocationSharing(_ roomViewController: RoomViewController) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
func threadsCoordinator(for roomViewController: RoomViewController, threadId: String?) -> ThreadsCoordinatorBridgePresenter? {
|
||||
guard let roomId = roomViewController.roomPreviewData?.roomId else {
|
||||
MXLog.error("[ExploreRoomCoordinator] Cannot create threads coordinator for room")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ThreadsCoordinatorBridgePresenter(
|
||||
session: session,
|
||||
roomId: roomId,
|
||||
threadId: threadId,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenter(presentingViewController: toPresentable())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ContactsPickerCoordinatorDelegate
|
||||
|
|
|
@ -55,6 +55,14 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
return self.navigationRouter.modules.last is MasterTabBarController
|
||||
}
|
||||
|
||||
private var detailUserIndicatorPresenter: UserIndicatorTypePresenterProtocol {
|
||||
guard let presenter = splitViewMasterPresentableDelegate?.detailUserIndicatorPresenter else {
|
||||
MXLog.debug("[TabBarCoordinator]: Missing detaul user indicator presenter")
|
||||
return UserIndicatorTypePresenter(presentingViewController: toPresentable())
|
||||
}
|
||||
return presenter
|
||||
}
|
||||
|
||||
private var indicators = [UserIndicator]()
|
||||
|
||||
// MARK: Public
|
||||
|
@ -415,8 +423,9 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
displayConfig = .default
|
||||
}
|
||||
|
||||
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: splitViewMasterPresentableDelegate?.detailUserIndicatorPresenter,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
session: roomNavigationParameters.mxSession,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
roomId: roomNavigationParameters.roomId,
|
||||
|
@ -435,7 +444,13 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
// RoomCoordinator will be presented by the split view.
|
||||
// As we don't know which navigation controller instance will be used,
|
||||
// give the NavigationRouterStore instance and let it find the associated navigation controller
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared, session: matrixSession, parentSpaceId: self.currentSpaceId, roomId: roomId, eventId: eventId, showSettingsInitially: false)
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
session: matrixSession,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
roomId: roomId,
|
||||
eventId: eventId,
|
||||
showSettingsInitially: false)
|
||||
|
||||
self.showRoom(with: roomCoordinatorParameters, completion: completion)
|
||||
}
|
||||
|
@ -445,7 +460,10 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
// RoomCoordinator will be presented by the split view
|
||||
// We don't which navigation controller instance will be used
|
||||
// Give the NavigationRouterStore instance and let it find the associated navigation controller if needed
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared, parentSpaceId: self.currentSpaceId, previewData: previewData)
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
previewData: previewData)
|
||||
|
||||
self.showRoom(with: roomCoordinatorParameters)
|
||||
}
|
||||
|
@ -453,7 +471,9 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
private func showRoomPreview(withNavigationParameters roomPreviewNavigationParameters: RoomPreviewNavigationParameters, completion: (() -> Void)?) {
|
||||
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
parentSpaceId: self.currentSpaceId, previewData: roomPreviewNavigationParameters.previewData)
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
previewData: roomPreviewNavigationParameters.previewData)
|
||||
|
||||
self.showRoom(with: roomCoordinatorParameters,
|
||||
stackOnSplitViewDetail: roomPreviewNavigationParameters.presentationParameters.stackAboveVisibleViews,
|
||||
|
@ -501,6 +521,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
|
||||
// create room coordinator
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
session: roomNavigationParameters.mxSession,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
roomId: roomNavigationParameters.roomId,
|
||||
|
@ -518,6 +539,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
|
||||
// create thread coordinator
|
||||
let threadCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
session: roomNavigationParameters.mxSession,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
roomId: roomNavigationParameters.roomId,
|
||||
|
@ -735,6 +757,7 @@ extension TabBarCoordinator: RoomCoordinatorDelegate {
|
|||
}
|
||||
|
||||
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
|
||||
userIndicatorPresenter: detailUserIndicatorPresenter,
|
||||
session: matrixSession,
|
||||
parentSpaceId: self.currentSpaceId,
|
||||
roomId: roomId,
|
||||
|
|
|
@ -132,6 +132,7 @@ final class ThreadsCoordinator: NSObject, ThreadsCoordinatorProtocol {
|
|||
private func createThreadCoordinator(forThreadId threadId: String) -> RoomCoordinator {
|
||||
let parameters = RoomCoordinatorParameters(navigationRouter: navigationRouter,
|
||||
navigationRouterStore: nil,
|
||||
userIndicatorPresenter: parameters.userIndicatorPresenter,
|
||||
session: parameters.session,
|
||||
parentSpaceId: nil,
|
||||
roomId: parameters.roomId,
|
||||
|
|
|
@ -47,6 +47,7 @@ final class ThreadsCoordinatorBridgePresenter: NSObject {
|
|||
private let session: MXSession
|
||||
private let roomId: String
|
||||
private let threadId: String?
|
||||
private let userIndicatorPresenter: UserIndicatorTypePresenterProtocol
|
||||
private var navigationType: NavigationType = .present
|
||||
private var coordinator: ThreadsCoordinator?
|
||||
|
||||
|
@ -63,10 +64,12 @@ final class ThreadsCoordinatorBridgePresenter: NSObject {
|
|||
/// - threadId: Thread identifier. Specified thread will be opened if provided, the thread list otherwise
|
||||
init(session: MXSession,
|
||||
roomId: String,
|
||||
threadId: String?) {
|
||||
threadId: String?,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.threadId = threadId
|
||||
self.userIndicatorPresenter = userIndicatorPresenter
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
@ -81,7 +84,8 @@ final class ThreadsCoordinatorBridgePresenter: NSObject {
|
|||
|
||||
let threadsCoordinatorParameters = ThreadsCoordinatorParameters(session: self.session,
|
||||
roomId: self.roomId,
|
||||
threadId: self.threadId)
|
||||
threadId: self.threadId,
|
||||
userIndicatorPresenter: userIndicatorPresenter)
|
||||
|
||||
let threadsCoordinator = ThreadsCoordinator(parameters: threadsCoordinatorParameters)
|
||||
threadsCoordinator.delegate = self
|
||||
|
@ -100,6 +104,7 @@ final class ThreadsCoordinatorBridgePresenter: NSObject {
|
|||
let threadsCoordinatorParameters = ThreadsCoordinatorParameters(session: self.session,
|
||||
roomId: self.roomId,
|
||||
threadId: self.threadId,
|
||||
userIndicatorPresenter: userIndicatorPresenter,
|
||||
navigationRouter: navigationRouter)
|
||||
|
||||
let threadsCoordinator = ThreadsCoordinator(parameters: threadsCoordinatorParameters)
|
||||
|
|
|
@ -33,13 +33,17 @@ struct ThreadsCoordinatorParameters {
|
|||
/// The navigation router that manage physical navigation
|
||||
let navigationRouter: NavigationRouterType
|
||||
|
||||
let userIndicatorPresenter: UserIndicatorTypePresenterProtocol
|
||||
|
||||
init(session: MXSession,
|
||||
roomId: String,
|
||||
threadId: String?,
|
||||
userIndicatorPresenter: UserIndicatorTypePresenterProtocol,
|
||||
navigationRouter: NavigationRouterType? = nil) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.threadId = threadId
|
||||
self.userIndicatorPresenter = userIndicatorPresenter
|
||||
self.navigationRouter = navigationRouter ?? NavigationRouter(navigationController: RiotNavigationController())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import CommonKit
|
||||
@testable import Riot
|
||||
import MatrixSDK
|
||||
|
||||
class UserIndicatorStoreTests: XCTestCase {
|
||||
class PresenterSpy: UserIndicatorTypePresenterProtocol {
|
||||
class ViewPresenter: UserIndicatorViewPresentable {
|
||||
func present() {}
|
||||
func dismiss() {}
|
||||
}
|
||||
|
||||
var queue = UserIndicatorQueue()
|
||||
var indicators = [UserIndicator]()
|
||||
|
||||
func present(_ type: UserIndicatorType) -> UserIndicator {
|
||||
let request = UserIndicatorRequest(presenter: ViewPresenter(), dismissal: .manual)
|
||||
let indicator = queue.add(request)
|
||||
indicators.append(indicator)
|
||||
return indicator
|
||||
}
|
||||
}
|
||||
|
||||
private var presenter: PresenterSpy!
|
||||
private var store: UserIndicatorStore!
|
||||
|
||||
override func setUp() {
|
||||
presenter = PresenterSpy()
|
||||
store = UserIndicatorStore(presenter: presenter)
|
||||
}
|
||||
|
||||
func test_presentWillStartIndicator() {
|
||||
let _ = presentLoading()
|
||||
XCTAssertEqual(indicator(at: 0)?.state, .executing)
|
||||
}
|
||||
|
||||
func test_cancelWillCompleteIndicator() {
|
||||
let cancel = presentLoading()
|
||||
|
||||
cancel()
|
||||
|
||||
XCTAssertEqual(indicator(at: 0)?.state, .completed)
|
||||
}
|
||||
|
||||
func test_cancelWillStartNextIndicator() {
|
||||
let cancel = presentLoading()
|
||||
let _ = presentLoading()
|
||||
|
||||
cancel()
|
||||
|
||||
XCTAssertEqual(indicator(at: 1)?.state, .executing)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func presentLoading() -> UserIndicatorCancel {
|
||||
return store.present(type: .loading(label: "xyz", isInteractionBlocking: false))
|
||||
}
|
||||
|
||||
private func indicator(at index: Int) -> UserIndicator? {
|
||||
guard index < presenter.indicators.count else {
|
||||
return nil
|
||||
}
|
||||
return presenter.indicators[index]
|
||||
}
|
||||
}
|
1
changelog.d/5604.change
Normal file
1
changelog.d/5604.change
Normal file
|
@ -0,0 +1 @@
|
|||
Room: New loading indicators when joining room
|
Loading…
Reference in a new issue