Merge pull request #5946 from vector-im/andy/5606_create_indicators

New loading indicators when creating a room
This commit is contained in:
Anderas
2022-03-30 11:05:43 +01:00
committed by GitHub
10 changed files with 55 additions and 80 deletions
@@ -1,56 +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
import CommonKit
/// A convenience objc-compatible wrapper around `UserIndicatorTypePresenterProtocol`.
///
/// This class wraps swift-only protocol by exposing multiple methods instead of accepting struct types
/// and it keeps a track of `UserIndicator`s instead of returning them to the caller.
@objc final class UserIndicatorPresenterWrapper: NSObject {
private let presenter: UserIndicatorTypePresenterProtocol
private var loadingIndicator: UserIndicator?
private var otherIndicators = [UserIndicator]()
init(presenter: UserIndicatorTypePresenterProtocol) {
self.presenter = presenter
}
@objc func presentLoadingIndicator() {
presentLoadingIndicator(label: VectorL10n.homeSyncing)
}
@objc func presentLoadingIndicator(label: String) {
guard loadingIndicator == nil else {
// The app is very liberal with calling `presentLoadingIndicator` (often not matched by corresponding `dismissLoadingIndicator`),
// so there is no reason to keep adding new indiciators if there is one already showing.
return
}
MXLog.debug("[UserIndicatorPresenterWrapper] Present loading indicator")
loadingIndicator = presenter.present(.loading(label: label, isInteractionBlocking: false))
}
@objc func dismissLoadingIndicator() {
MXLog.debug("[UserIndicatorPresenterWrapper] Dismiss loading indicator")
loadingIndicator = nil
}
@objc func presentSuccess(label: String) {
presenter.present(.success(label: label)).store(in: &otherIndicators)
}
}
@@ -19,7 +19,7 @@
@class RootTabEmptyView;
@class AnalyticsScreenTracker;
@class UserIndicatorPresenterWrapper;
@class UserIndicatorStore;
/**
Notification to be posted when recents data is ready. Notification object will be the RecentsViewController instance.
@@ -98,9 +98,10 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification;
@property (nonatomic) AnalyticsScreenTracker *screenTracker;
/**
Presenter for displaying app-wide user indicators. If not set, the view controller will use legacy activity indicators
A store of user indicators that lets the room present and dismiss indicators without
worrying about the presentation context or memory management.
*/
@property (nonatomic, strong) UserIndicatorPresenterWrapper *indicatorPresenter;
@property (nonatomic, strong) UserIndicatorStore *userIndicatorStore;
/**
Return the sticky header for the specified section of the table view
@@ -69,6 +69,9 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
__weak id kThemeServiceDidChangeThemeNotificationObserver;
// Cancel handler of any ongoing loading indicator
UserIndicatorCancel loadingIndicatorCancel;
}
@property (nonatomic, strong) CreateRoomCoordinatorBridgePresenter *createRoomCoordinatorBridgePresenter;
@@ -1305,7 +1308,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
[self.indicatorPresenter presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
[self.userIndicatorStore presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
// Force table refresh
[self cancelEditionMode:YES];
}
@@ -2450,28 +2453,35 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
#pragma mark - Activity Indicator
- (BOOL)providesCustomActivityIndicator {
return self.indicatorPresenter != nil;
return self.userIndicatorStore != nil;
}
- (void)startActivityIndicatorWithLabel:(NSString *)label {
if (self.indicatorPresenter && isViewVisible) {
[self.indicatorPresenter presentLoadingIndicatorWithLabel:label];
if (self.userIndicatorStore && isViewVisible) {
// The app is very liberal with calling `startActivityIndicator` (often not matched by corresponding `stopActivityIndicator`),
// so there is no reason to keep adding new indicators if there is one already showing.
if (loadingIndicatorCancel) {
return;
}
MXLogDebug(@"[RecentsViewController] Present loading indicator")
loadingIndicatorCancel = [self.userIndicatorStore presentLoadingWithLabel:label isInteractionBlocking:NO];
} else {
[super startActivityIndicator];
}
}
- (void)startActivityIndicator {
if (self.indicatorPresenter && isViewVisible) {
[self.indicatorPresenter presentLoadingIndicator];
} else {
[super startActivityIndicator];
}
[self startActivityIndicatorWithLabel:[VectorL10n homeSyncing]];
}
- (void)stopActivityIndicator {
if (self.indicatorPresenter) {
[self.indicatorPresenter dismissLoadingIndicator];
if (self.userIndicatorStore) {
if (loadingIndicatorCancel) {
MXLogDebug(@"[RecentsViewController] Present loading indicator")
loadingIndicatorCancel();
loadingIndicatorCancel = nil;
}
} else {
[super stopActivityIndicator];
}
@@ -39,8 +39,8 @@ import CommonKit
}
}
/// Present a new type of user indicator, such as loading spinner or success message.
/// To remove an indicator, call the returned `UserIndicatorCancel` function
/// Present a loading indicator.
/// To remove the 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 {
@@ -51,4 +51,12 @@ import CommonKit
)
)
}
/// Present a success message that will be automatically dismissed after a few seconds.
///
/// Note: This is a convenience function callable by objective-c code
@objc func presentSuccess(label: String) {
let indicator = presenter.present(.success(label: label))
indicators.append(indicator)
}
}
@@ -17,6 +17,7 @@
*/
import UIKit
import CommonKit
final class EnterNewRoomDetailsViewController: UIViewController {
@@ -47,7 +48,9 @@ final class EnterNewRoomDetailsViewController: UIViewController {
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private var userIndicatorPresenter: UserIndicatorTypePresenterProtocol!
private var loadingIndicator: UserIndicator?
private lazy var createBarButtonItem: MXKBarButtonItem = {
let title: String
switch viewModel.actionType {
@@ -262,7 +265,7 @@ final class EnterNewRoomDetailsViewController: UIViewController {
self.setupViews()
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.mainTableView)
self.activityPresenter = ActivityIndicatorPresenter()
self.userIndicatorPresenter = UserIndicatorTypePresenter(presentingViewController: self)
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
@@ -352,11 +355,11 @@ final class EnterNewRoomDetailsViewController: UIViewController {
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
loadingIndicator = userIndicatorPresenter.present(.loading(label: VectorL10n.createRoomProcessing, isInteractionBlocking: true))
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
loadingIndicator = nil
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
@@ -115,6 +115,7 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
fatalError("[EnterNewRoomDetailsViewModel] createRoom: room name cannot be nil.")
}
viewState = .loading
currentOperation = session.createRoom(
withName: roomName,
joinRule: roomCreationParameters.joinRule,
@@ -125,6 +126,8 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
completion: { response in
switch response {
case .success(let room):
self.viewState = .loaded
if let parentSpace = self.parentSpace {
self.add(room, to: parentSpace)
} else {
+4 -4
View File
@@ -239,7 +239,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
homeViewController.tabBarItem.tag = Int(TABBAR_HOME_INDEX)
homeViewController.tabBarItem.image = homeViewController.tabBarItem.image
homeViewController.accessibilityLabel = VectorL10n.titleHome
homeViewController.indicatorPresenter = UserIndicatorPresenterWrapper(presenter: indicatorPresenter)
homeViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController)
return wrapperViewController
@@ -249,7 +249,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
let favouritesViewController: FavouritesViewController = FavouritesViewController.instantiate()
favouritesViewController.tabBarItem.tag = Int(TABBAR_FAVOURITES_INDEX)
favouritesViewController.accessibilityLabel = VectorL10n.titleFavourites
favouritesViewController.indicatorPresenter = UserIndicatorPresenterWrapper(presenter: indicatorPresenter)
favouritesViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
return favouritesViewController
}
@@ -257,7 +257,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
let peopleViewController: PeopleViewController = PeopleViewController.instantiate()
peopleViewController.tabBarItem.tag = Int(TABBAR_PEOPLE_INDEX)
peopleViewController.accessibilityLabel = VectorL10n.titlePeople
peopleViewController.indicatorPresenter = UserIndicatorPresenterWrapper(presenter: indicatorPresenter)
peopleViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
return peopleViewController
}
@@ -265,7 +265,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
let roomsViewController: RoomsViewController = RoomsViewController.instantiate()
roomsViewController.tabBarItem.tag = Int(TABBAR_ROOMS_INDEX)
roomsViewController.accessibilityLabel = VectorL10n.titleRooms
roomsViewController.indicatorPresenter = UserIndicatorPresenterWrapper(presenter: indicatorPresenter)
roomsViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
return roomsViewController
}