Merge pull request #6660 from vector-im/gil/6513-Glitchy_room_list_header_when_scrolling

Glitchy room list header when scrolling
This commit is contained in:
Gil Eluard
2022-09-05 10:16:56 +02:00
committed by GitHub
24 changed files with 1576 additions and 336 deletions
+2 -1
View File
@@ -28,6 +28,7 @@
@protocol Configurable;
@protocol LegacyAppDelegateDelegate;
@protocol SplitViewMasterViewControllerProtocol;
@class CallBar;
@class CallPresenter;
@class RoomNavigationParameters;
@@ -69,7 +70,7 @@ UINavigationControllerDelegate
/**
Application main view controller
*/
@property (nonatomic, readonly) MasterTabBarController *masterTabBarController;
@property (nonatomic, readonly) UIViewController<SplitViewMasterViewControllerProtocol>* masterTabBarController;
@property (strong, nonatomic) UIWindow *window;
@@ -1714,7 +1714,10 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
- (void)recentsListServiceDidChangeData:(id<RecentsListServiceProtocol>)service
totalCountsChanged:(BOOL)totalCountsChanged
{
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
if (!BuildSettings.isNewAppLayoutActivated)
{
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
}
}
- (void)recentsListServiceDidChangeData:(id<RecentsListServiceProtocol>)service
@@ -373,8 +373,6 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
- (void)refreshRecentsTable
{
MXLogDebug(@"[RecentsViewController]: Refreshing recents table view")
if (!self.recentsUpdateEnabled)
{
isRefreshNeeded = YES;
@@ -384,7 +382,11 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
isRefreshNeeded = NO;
// Refresh the tabBar icon badges
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
if (!BuildSettings.isNewAppLayoutActivated)
{
// Refresh the tabBar icon badges
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
}
// do not refresh if there is a pending recent drag and drop
if (movingCellPath)
@@ -1103,9 +1105,12 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[self refreshRecentsTable];
}
// Since we've enabled room list pagination, `refreshRecentsTable` not called in this case.
// Refresh tab bar badges separately.
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
if (!BuildSettings.isNewAppLayoutActivated)
{
// Since we've enabled room list pagination, `refreshRecentsTable` not called in this case.
// Refresh tab bar badges separately.
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
}
[self showEmptyViewIfNeeded];
@@ -1513,6 +1518,12 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (!self.recentsSearchBar)
{
[super scrollViewDidScroll:scrollView];
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshStickyHeadersContainersHeight];
@@ -88,7 +88,7 @@
{
self.lastEventDescription.text = roomCellData.lastEventTextMessage;
}
self.unsentImageView.hidden = roomCellData.roomSummary.sentStatus == MXRoomSummarySentStatusOk;
self.lastEventDecriptionLabelTrailingConstraint.constant = self.unsentImageView.hidden ? 10 : 30;
@@ -96,17 +96,17 @@
if (roomCellData.hasUnread)
{
self.missedNotifAndUnreadIndicator.hidden = NO;
if (0 < roomCellData.notificationCount)
{
self.missedNotifAndUnreadIndicator.backgroundColor = roomCellData.highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor;
self.missedNotifAndUnreadBadgeBgView.hidden = NO;
self.missedNotifAndUnreadBadgeBgView.backgroundColor = self.missedNotifAndUnreadIndicator.backgroundColor;
self.missedNotifAndUnreadBadgeLabel.text = roomCellData.notificationCountStringValue;
[self.missedNotifAndUnreadBadgeLabel sizeToFit];
self.missedNotifAndUnreadBadgeBgViewWidthConstraint.constant = self.missedNotifAndUnreadBadgeLabel.frame.size.width + 18;
}
else
@@ -120,8 +120,8 @@
else
{
self.lastEventDate.textColor = ThemeService.shared.theme.textSecondaryColor;
// The room title is not bold anymore
// The room title is not bold anymore
self.roomTitle.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium];
}
@@ -0,0 +1,817 @@
//
// 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
/// AllChatsCoordinator input parameters
class AllChatsCoordinatorParameters {
let userSessionsService: UserSessionsService
let appNavigator: AppNavigatorProtocol
init(userSessionsService: UserSessionsService, appNavigator: AppNavigatorProtocol) {
self.userSessionsService = userSessionsService
self.appNavigator = appNavigator
}
}
class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol {
// MARK: Properties
// MARK: Private
private let parameters: AllChatsCoordinatorParameters
private let activityIndicatorPresenter: ActivityIndicatorPresenterType
private let indicatorPresenter: UserIndicatorTypePresenterProtocol
private let userIndicatorStore: UserIndicatorStore
private var appStateIndicatorCancel: UserIndicatorCancel?
private var appSateIndicator: UserIndicator?
// Indicate if the Coordinator has started once
private var hasStartedOnce: Bool {
return self.allChatsViewController != nil
}
// TODO: Move MasterTabBarController navigation code here
private var allChatsViewController: AllChatsViewController!
// TODO: Embed UINavigationController in each tab like recommended by Apple and remove these properties. UITabBarViewController shoud not be embed in a UINavigationController (https://github.com/vector-im/riot-ios/issues/3086).
private let navigationRouter: NavigationRouterType
private var currentSpaceId: String?
private weak var versionCheckCoordinator: VersionCheckCoordinator?
private var currentMatrixSession: MXSession? {
return parameters.userSessionsService.mainUserSession?.matrixSession
}
private var isAllChatsControllerTopMostController: Bool {
return self.navigationRouter.modules.last is AllChatsViewController
}
private var detailUserIndicatorPresenter: UserIndicatorTypePresenterProtocol {
guard let presenter = splitViewMasterPresentableDelegate?.detailUserIndicatorPresenter else {
MXLog.debug("[AllChatsCoordinator]: Missing defautl user indicator presenter")
return UserIndicatorTypePresenter(presentingViewController: toPresentable())
}
return presenter
}
private var indicators = [UserIndicator]()
private var signOutAlertPresenter = SignOutAlertPresenter()
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: SplitViewMasterCoordinatorDelegate?
weak var splitViewMasterPresentableDelegate: SplitViewMasterPresentableDelegate?
// MARK: - Setup
init(parameters: AllChatsCoordinatorParameters) {
self.parameters = parameters
let masterNavigationController = RiotNavigationController()
self.navigationRouter = NavigationRouter(navigationController: masterNavigationController)
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
self.indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: masterNavigationController)
self.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
}
// MARK: - Public methods
func start() {
self.start(with: nil)
}
func start(with spaceId: String?) {
// If start has been done once do not setup view controllers again
if self.hasStartedOnce == false {
signOutAlertPresenter.delegate = self
let allChatsViewController = AllChatsViewController.instantiate()
allChatsViewController.allChatsDelegate = self
allChatsViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
createLeftButtonItem(for: allChatsViewController)
self.allChatsViewController = allChatsViewController
self.navigationRouter.setRootModule(allChatsViewController)
// Add existing Matrix sessions if any
for userSession in self.parameters.userSessionsService.userSessions {
self.addMatrixSessionToAllChatsController(userSession.matrixSession)
}
self.registerUserSessionsServiceNotifications()
self.registerSessionChange()
let versionCheckCoordinator = createVersionCheckCoordinator(withRootViewController: allChatsViewController, bannerPresentrer: allChatsViewController)
versionCheckCoordinator.start()
self.add(childCoordinator: versionCheckCoordinator)
}
self.allChatsViewController?.switchSpace(withId: spaceId)
self.currentSpaceId = spaceId
}
func toPresentable() -> UIViewController {
return self.navigationRouter.toPresentable()
}
func releaseSelectedItems() {
self.allChatsViewController.releaseSelectedItem()
}
func popToHome(animated: Bool, completion: (() -> Void)?) {
// Force back to the main screen if this is not the one that is displayed
if allChatsViewController != self.navigationRouter.modules.last?.toPresentable() {
// Listen to the masterNavigationController changes
// We need to be sure that allChatsViewController is back to the screen
// If the AllChatsViewController is not visible because there is a modal above it
// but still the top view controller of navigation controller
if self.isAllChatsControllerTopMostController {
completion?()
} else {
// Otherwise AllChatsViewController is not the top controller of the navigation controller
// Waiting for `self.navigationRouter` popping to AllChatsViewController
var token: NSObjectProtocol?
token = NotificationCenter.default.addObserver(forName: NavigationRouter.didPopModule, object: self.navigationRouter, queue: OperationQueue.main) { [weak self] (notification) in
guard let self = self else {
return
}
// If AllChatsViewController is now the top most controller in navigation controller stack call the completion
if self.isAllChatsControllerTopMostController {
completion?()
if let token = token {
NotificationCenter.default.removeObserver(token)
}
}
}
// Pop to root view controller
self.navigationRouter.popToRootModule(animated: animated)
}
} else {
// the AllChatsViewController is already visible
completion?()
}
}
func showErroIndicator(with error: Error) {
let error = error as NSError
// Ignore fake error, or connection cancellation error
guard error.domain != NSURLErrorDomain || error.code != NSURLErrorCancelled else {
return
}
// Ignore GDPR Consent not given error. Already caught by kMXHTTPClientUserConsentNotGivenErrorNotification observation
let mxError = MXError.isMXError(error) ? MXError(nsError: error) : nil
guard mxError?.errcode != kMXErrCodeStringConsentNotGiven else {
return
}
let msg = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String
let localizedDescription = error.userInfo[NSLocalizedDescriptionKey] as? String
let title = (error.userInfo[NSLocalizedFailureReasonErrorKey] as? String) ?? (msg ?? (localizedDescription ?? VectorL10n.error))
indicators.append(self.indicatorPresenter.present(.failure(label: title)))
}
func showAppStateIndicator(with text: String, icon: UIImage?) {
hideAppStateIndicator()
appSateIndicator = self.indicatorPresenter.present(.custom(label: text, icon: icon))
}
func hideAppStateIndicator() {
appSateIndicator?.cancel()
appSateIndicator = nil
}
// MARK: - SplitViewMasterPresentable
var selectedNavigationRouter: NavigationRouterType? {
return self.navigationRouter
}
// MARK: Split view
/// If the split view is collapsed (one column visible) it will push the Presentable on the primary navigation controller, otherwise it will show the Presentable as the secondary view of the split view.
private func replaceSplitViewDetails(with presentable: Presentable, popCompletion: (() -> Void)? = nil) {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToReplaceDetailWith: presentable, popCompletion: popCompletion)
}
/// If the split view is collapsed (one column visible) it will push the Presentable on the primary navigation controller, otherwise it will show the Presentable as the secondary view of the split view on top of existing views.
private func stackSplitViewDetails(with presentable: Presentable, popCompletion: (() -> Void)? = nil) {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToStack: presentable, popCompletion: popCompletion)
}
private func showSplitViewDetails(with presentable: Presentable, stackedOnSplitViewDetail: Bool, popCompletion: (() -> Void)? = nil) {
if stackedOnSplitViewDetail {
self.stackSplitViewDetails(with: presentable, popCompletion: popCompletion)
} else {
self.replaceSplitViewDetails(with: presentable, popCompletion: popCompletion)
}
}
private func showSplitViewDetails(with modules: [NavigationModule], stack: Bool) {
if stack {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToStack: modules)
} else {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToReplaceDetailsWith: modules)
}
}
private func resetSplitViewDetails() {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentableWantsToResetDetail(self)
}
// MARK: UserSessions management
private func registerUserSessionsServiceNotifications() {
// Listen only notifications from the current UserSessionsService instance
let userSessionService = self.parameters.userSessionsService
NotificationCenter.default.addObserver(self, selector: #selector(userSessionsServiceDidAddUserSession(_:)), name: UserSessionsService.didAddUserSession, object: userSessionService)
NotificationCenter.default.addObserver(self, selector: #selector(userSessionsServiceWillRemoveUserSession(_:)), name: UserSessionsService.willRemoveUserSession, object: userSessionService)
}
@objc private func userSessionsServiceDidAddUserSession(_ notification: Notification) {
guard let userSession = notification.userInfo?[UserSessionsService.NotificationUserInfoKey.userSession] as? UserSession else {
return
}
self.addMatrixSessionToAllChatsController(userSession.matrixSession)
}
@objc private func userSessionsServiceWillRemoveUserSession(_ notification: Notification) {
guard let userSession = notification.userInfo?[UserSessionsService.NotificationUserInfoKey.userSession] as? UserSession else {
return
}
self.removeMatrixSessionFromAllChatsController(userSession.matrixSession)
}
// MARK: - Matrix Session management
// TODO: Remove Matrix session handling from the view controller
private func addMatrixSessionToAllChatsController(_ matrixSession: MXSession) {
MXLog.debug("[TabBarCoordinator] masterTabBarController.addMatrixSession")
self.allChatsViewController.addMatrixSession(matrixSession)
}
// TODO: Remove Matrix session handling from the view controller
private func removeMatrixSessionFromAllChatsController(_ matrixSession: MXSession) {
MXLog.debug("[TabBarCoordinator] masterTabBarController.removeMatrixSession")
self.allChatsViewController.removeMatrixSession(matrixSession)
}
private func registerSessionChange() {
NotificationCenter.default.addObserver(self, selector: #selector(sessionDidSync(_:)), name: NSNotification.Name.mxSessionDidSync, object: nil)
}
@objc private func sessionDidSync(_ notification: Notification) {
updateAvatarButtonItem()
}
// MARK: Navigation
private func showSettings() {
let viewController = self.createSettingsViewController()
self.navigationRouter.push(viewController, animated: true, popCompletion: nil)
}
private func showContactDetails(with contact: MXKContact, presentationParameters: ScreenPresentationParameters) {
let coordinatorParameters = ContactDetailsCoordinatorParameters(contact: contact)
let coordinator = ContactDetailsCoordinator(parameters: coordinatorParameters)
coordinator.start()
self.add(childCoordinator: coordinator)
self.showSplitViewDetails(with: coordinator, stackedOnSplitViewDetail: presentationParameters.stackAboveVisibleViews) { [weak self] in
self?.remove(childCoordinator: coordinator)
}
}
// MARK: Navigation bar items management
private weak var avatarMenuView: AvatarView?
private weak var avatarMenuButton: UIButton?
private func createLeftButtonItem(for viewController: UIViewController) {
createAvatarButtonItem(for: viewController)
}
private func createAvatarButtonItem(for viewController: UIViewController) {
var actions: [UIMenuElement] = []
actions.append(UIAction(title: VectorL10n.allChatsUserMenuSettings, image: UIImage(systemName: "gearshape")) { [weak self] action in
self?.showSettings()
})
var subMenuActions: [UIAction] = []
if BuildSettings.sideMenuShowInviteFriends {
subMenuActions.append(UIAction(title: VectorL10n.sideMenuActionInviteFriends, image: UIImage(systemName: "square.and.arrow.up.fill")) { [weak self] action in
self?.showInviteFriends(from: nil)
})
}
subMenuActions.append(UIAction(title: VectorL10n.sideMenuActionFeedback, image: UIImage(systemName: "questionmark.circle")) { [weak self] action in
self?.showBugReport()
})
actions.append(UIMenu(title: "", options: .displayInline, children: subMenuActions))
actions.append(UIMenu(title: "", options: .displayInline, children: [
UIAction(title: VectorL10n.settingsSignOut, image: UIImage(systemName: "rectangle.portrait.and.arrow.right.fill"), attributes: .destructive) { [weak self] action in
self?.signOut()
}
]))
let menu = UIMenu(options: .displayInline, children: actions)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 36, height: 36))
view.backgroundColor = .clear
let button: UIButton = UIButton(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)))
button.setImage(Asset.Images.tabPeople.image, for: .normal)
button.menu = menu
button.showsMenuAsPrimaryAction = true
button.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(button)
self.avatarMenuButton = button
let avatarView = UserAvatarView(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)))
avatarView.isUserInteractionEnabled = false
avatarView.update(theme: ThemeService.shared().theme)
avatarView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(avatarView)
self.avatarMenuView = avatarView
if let avatar = userAvatarViewData(from: currentMatrixSession) {
avatarView.fill(with: avatar)
button.setImage(nil, for: .normal)
}
viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view)
}
private func updateAvatarButtonItem() {
guard let avatarView = avatarMenuView, let button = avatarMenuButton, let avatar = userAvatarViewData(from: currentMatrixSession) else {
return
}
button.setImage(nil, for: .normal)
avatarView.fill(with: avatar)
}
private func showRoom(withId roomId: String, eventId: String? = nil) {
guard let matrixSession = self.parameters.userSessionsService.mainUserSession?.matrixSession else {
return
}
self.showRoom(with: roomId, eventId: eventId, matrixSession: matrixSession)
}
private func showRoom(withNavigationParameters roomNavigationParameters: RoomNavigationParameters, completion: (() -> Void)?) {
if let threadParameters = roomNavigationParameters.threadParameters, threadParameters.stackRoomScreen {
showRoomAndThread(with: roomNavigationParameters,
completion: completion)
} else {
let threadId = roomNavigationParameters.threadParameters?.threadId
let displayConfig: RoomDisplayConfiguration
if threadId != nil {
displayConfig = .forThreads
} else {
displayConfig = .default
}
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
userIndicatorPresenter: detailUserIndicatorPresenter,
session: roomNavigationParameters.mxSession,
parentSpaceId: self.currentSpaceId,
roomId: roomNavigationParameters.roomId,
eventId: roomNavigationParameters.eventId,
threadId: threadId,
showSettingsInitially: roomNavigationParameters.showSettingsInitially,
displayConfiguration: displayConfig,
autoJoinInvitedRoom: roomNavigationParameters.autoJoinInvitedRoom)
self.showRoom(with: roomCoordinatorParameters,
stackOnSplitViewDetail: roomNavigationParameters.presentationParameters.stackAboveVisibleViews,
completion: completion)
}
}
private func showRoom(with roomId: String, eventId: String?, matrixSession: MXSession, completion: (() -> Void)? = nil) {
// 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,
userIndicatorPresenter: detailUserIndicatorPresenter,
session: matrixSession,
parentSpaceId: self.currentSpaceId,
roomId: roomId,
eventId: eventId,
showSettingsInitially: false)
self.showRoom(with: roomCoordinatorParameters, completion: completion)
}
private func showRoomPreview(with previewData: RoomPreviewData) {
// 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,
userIndicatorPresenter: detailUserIndicatorPresenter,
parentSpaceId: self.currentSpaceId,
previewData: previewData)
self.showRoom(with: roomCoordinatorParameters)
}
private func showRoomPreview(withNavigationParameters roomPreviewNavigationParameters: RoomPreviewNavigationParameters, completion: (() -> Void)?) {
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
userIndicatorPresenter: detailUserIndicatorPresenter,
parentSpaceId: self.currentSpaceId,
previewData: roomPreviewNavigationParameters.previewData)
self.showRoom(with: roomCoordinatorParameters,
stackOnSplitViewDetail: roomPreviewNavigationParameters.presentationParameters.stackAboveVisibleViews,
completion: completion)
}
private func showRoom(with parameters: RoomCoordinatorParameters,
stackOnSplitViewDetail: Bool = false,
completion: (() -> Void)? = nil) {
// try to find the desired room screen in the stack
if let roomCoordinator = self.splitViewMasterPresentableDelegate?.detailModules.last(where: { presentable in
guard let roomCoordinator = presentable as? RoomCoordinatorProtocol else {
return false
}
return roomCoordinator.roomId == parameters.roomId
&& roomCoordinator.threadId == parameters.threadId
&& roomCoordinator.mxSession == parameters.session
}) as? RoomCoordinatorProtocol {
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentable(self, wantsToPopTo: roomCoordinator)
// go to a specific event if provided
if let eventId = parameters.eventId {
roomCoordinator.start(withEventId: eventId, completion: completion)
} else {
completion?()
}
return
}
let coordinator = RoomCoordinator(parameters: parameters)
coordinator.delegate = self
coordinator.start(withCompletion: completion)
self.add(childCoordinator: coordinator)
self.showSplitViewDetails(with: coordinator, stackedOnSplitViewDetail: stackOnSplitViewDetail) { [weak self] in
// NOTE: The RoomDataSource releasing is handled in SplitViewCoordinator
self?.remove(childCoordinator: coordinator)
}
}
private func showRoomAndThread(with roomNavigationParameters: RoomNavigationParameters,
completion: (() -> Void)? = nil) {
self.activityIndicatorPresenter.presentActivityIndicator(on: toPresentable().view, animated: false)
let dispatchGroup = DispatchGroup()
// create room coordinator
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
userIndicatorPresenter: detailUserIndicatorPresenter,
session: roomNavigationParameters.mxSession,
parentSpaceId: self.currentSpaceId,
roomId: roomNavigationParameters.roomId,
eventId: nil,
threadId: nil,
showSettingsInitially: false)
dispatchGroup.enter()
let roomCoordinator = RoomCoordinator(parameters: roomCoordinatorParameters)
roomCoordinator.delegate = self
roomCoordinator.start {
dispatchGroup.leave()
}
self.add(childCoordinator: roomCoordinator)
// create thread coordinator
let threadCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
userIndicatorPresenter: detailUserIndicatorPresenter,
session: roomNavigationParameters.mxSession,
parentSpaceId: self.currentSpaceId,
roomId: roomNavigationParameters.roomId,
eventId: roomNavigationParameters.eventId,
threadId: roomNavigationParameters.threadParameters?.threadId,
showSettingsInitially: false,
displayConfiguration: .forThreads)
dispatchGroup.enter()
let threadCoordinator = RoomCoordinator(parameters: threadCoordinatorParameters)
threadCoordinator.delegate = self
threadCoordinator.start {
dispatchGroup.leave()
}
self.add(childCoordinator: threadCoordinator)
dispatchGroup.notify(queue: .main) { [weak self] in
guard let self = self else { return }
let modules: [NavigationModule] = [
NavigationModule(presentable: roomCoordinator, popCompletion: { [weak self] in
// NOTE: The RoomDataSource releasing is handled in SplitViewCoordinator
self?.remove(childCoordinator: roomCoordinator)
}),
NavigationModule(presentable: threadCoordinator, popCompletion: { [weak self] in
// NOTE: The RoomDataSource releasing is handled in SplitViewCoordinator
self?.remove(childCoordinator: threadCoordinator)
})
]
self.showSplitViewDetails(with: modules,
stack: roomNavigationParameters.presentationParameters.stackAboveVisibleViews)
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
}
}
// MARK: Sign out process
private func signOut() {
guard let keyBackup = currentMatrixSession?.crypto.backup else {
return
}
signOutAlertPresenter.present(for: keyBackup.state,
areThereKeysToBackup: keyBackup.hasKeysToBackup,
from: self.allChatsViewController,
sourceView: nil,
animated: true)
}
// MARK: - SecureBackupSetupCoordinatorBridgePresenter
private var secureBackupSetupCoordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter?
private var crossSigningSetupCoordinatorBridgePresenter: CrossSigningSetupCoordinatorBridgePresenter?
private func showSecureBackupSetupFromSignOutFlow() {
if canSetupSecureBackup {
setupSecureBackup2()
} else {
// Set up cross-signing first
setupCrossSigning(title: VectorL10n.secureKeyBackupSetupIntroTitle,
message: VectorL10n.securitySettingsUserPasswordDescription) { [weak self] result in
switch result {
case .success(let isCompleted):
if isCompleted {
self?.setupSecureBackup2()
}
case .failure:
break
}
}
}
}
private var canSetupSecureBackup: Bool {
return currentMatrixSession?.vc_canSetupSecureBackup() ?? false
}
private func setupSecureBackup2() {
guard let session = currentMatrixSession else {
return
}
let secureBackupSetupCoordinatorBridgePresenter = SecureBackupSetupCoordinatorBridgePresenter(session: session, allowOverwrite: true)
secureBackupSetupCoordinatorBridgePresenter.delegate = self
secureBackupSetupCoordinatorBridgePresenter.present(from: allChatsViewController, animated: true)
self.secureBackupSetupCoordinatorBridgePresenter = secureBackupSetupCoordinatorBridgePresenter
}
private func setupCrossSigning(title: String, message: String, completion: @escaping (Result<Bool, Error>) -> Void) {
guard let session = currentMatrixSession else {
return
}
allChatsViewController.startActivityIndicator()
allChatsViewController.view.isUserInteractionEnabled = false
let dismissAnimation = { [weak self] in
guard let self = self else { return }
self.allChatsViewController.stopActivityIndicator()
self.allChatsViewController.view.isUserInteractionEnabled = true
self.crossSigningSetupCoordinatorBridgePresenter?.dismiss(animated: true, completion: {
self.crossSigningSetupCoordinatorBridgePresenter = nil
})
}
let crossSigningSetupCoordinatorBridgePresenter = CrossSigningSetupCoordinatorBridgePresenter(session: session)
crossSigningSetupCoordinatorBridgePresenter.present(with: title, message: message, from: allChatsViewController, animated: true) {
dismissAnimation()
completion(.success(true))
} cancel: {
dismissAnimation()
completion(.success(false))
} failure: { error in
dismissAnimation()
completion(.failure(error))
}
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter
}
// MARK: - Private methods
private func createVersionCheckCoordinator(withRootViewController rootViewController: UIViewController, bannerPresentrer: BannerPresentationProtocol) -> VersionCheckCoordinator {
let versionCheckCoordinator = VersionCheckCoordinator(rootViewController: rootViewController,
bannerPresenter: bannerPresentrer,
themeService: ThemeService.shared())
return versionCheckCoordinator
}
private func showInviteFriends(from sourceView: UIView?) {
let myUserId = self.parameters.userSessionsService.mainUserSession?.userId ?? ""
let inviteFriendsPresenter = InviteFriendsPresenter()
inviteFriendsPresenter.present(for: myUserId, from: self.navigationRouter.toPresentable(), sourceView: sourceView, animated: true)
}
private func showBugReport() {
let bugReportViewController = BugReportViewController()
// Show in fullscreen to animate presentation along side menu dismiss
bugReportViewController.modalPresentationStyle = .fullScreen
bugReportViewController.modalTransitionStyle = .crossDissolve
self.navigationRouter.present(bugReportViewController, animated: true)
}
private func userAvatarViewData(from mxSession: MXSession?) -> UserAvatarViewData? {
guard let mxSession = mxSession, let userId = mxSession.myUserId, let mediaManager = mxSession.mediaManager, let myUser = mxSession.myUser else {
return nil
}
let userDisplayName = myUser.displayname
let avatarUrl = myUser.avatarUrl
return UserAvatarViewData(userId: userId,
displayName: userDisplayName,
avatarUrl: avatarUrl,
mediaManager: mediaManager)
}
private func createUnifiedSearchController() -> UnifiedSearchViewController {
let viewController: UnifiedSearchViewController = UnifiedSearchViewController.instantiate()
viewController.loadViewIfNeeded()
for userSession in self.parameters.userSessionsService.userSessions {
viewController.addMatrixSession(userSession.matrixSession)
}
return viewController
}
private func createSettingsViewController() -> SettingsViewController {
let viewController: SettingsViewController = SettingsViewController.instantiate()
viewController.loadViewIfNeeded()
return viewController
}
}
// MARK: - SignOutAlertPresenterDelegate
extension AllChatsCoordinator: SignOutAlertPresenterDelegate {
func signOutAlertPresenterDidTapSignOutAction(_ presenter: SignOutAlertPresenter) {
// Prevent user to perform user interaction in settings when sign out
// TODO: Prevent user interaction in all application (navigation controller and split view controller included)
allChatsViewController.view.isUserInteractionEnabled = false
allChatsViewController.startActivityIndicator()
AppDelegate.theDelegate().logout(withConfirmation: false) { [weak self] isLoggedOut in
self?.allChatsViewController.stopActivityIndicator()
self?.allChatsViewController.view.isUserInteractionEnabled = true
}
}
func signOutAlertPresenterDidTapBackupAction(_ presenter: SignOutAlertPresenter) {
showSecureBackupSetupFromSignOutFlow()
}
}
// MARK: - SecureBackupSetupCoordinatorBridgePresenterDelegate
extension AllChatsCoordinator: SecureBackupSetupCoordinatorBridgePresenterDelegate {
func secureBackupSetupCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter) {
coordinatorBridgePresenter.dismiss(animated: true) {
self.secureBackupSetupCoordinatorBridgePresenter = nil
}
}
func secureBackupSetupCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter) {
coordinatorBridgePresenter.dismiss(animated: true) {
self.secureBackupSetupCoordinatorBridgePresenter = nil
}
}
}
// MARK: - AllChatsViewControllerDelegate
extension AllChatsCoordinator: AllChatsViewControllerDelegate {
func allChatsViewControllerDidCompleteAuthentication(_ allChatsViewController: AllChatsViewController) {
self.delegate?.splitViewMasterCoordinatorDidCompleteAuthentication(self)
}
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectRoomWithParameters roomNavigationParameters: RoomNavigationParameters, completion: @escaping () -> Void) {
self.showRoom(withNavigationParameters: roomNavigationParameters, completion: completion)
}
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectRoomPreviewWithParameters roomPreviewNavigationParameters: RoomPreviewNavigationParameters, completion: @escaping () -> Void) {
self.showRoomPreview(withNavigationParameters: roomPreviewNavigationParameters, completion: completion)
}
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectContact contact: MXKContact, with presentationParameters: ScreenPresentationParameters) {
self.showContactDetails(with: contact, presentationParameters: presentationParameters)
}
}
// MARK: - RoomCoordinatorDelegate
extension AllChatsCoordinator: RoomCoordinatorDelegate {
func roomCoordinatorDidDismissInteractively(_ coordinator: RoomCoordinatorProtocol) {
self.remove(childCoordinator: coordinator)
}
func roomCoordinatorDidLeaveRoom(_ coordinator: RoomCoordinatorProtocol) {
// For the moment when a room is left, reset the split detail with placeholder
self.resetSplitViewDetails()
indicatorPresenter
.present(.success(label: VectorL10n.roomParticipantsLeaveSuccess))
.store(in: &indicators)
}
func roomCoordinatorDidCancelRoomPreview(_ coordinator: RoomCoordinatorProtocol) {
self.navigationRouter.popModule(animated: true)
}
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didSelectRoomWithId roomId: String, eventId: String?) {
self.showRoom(withId: roomId, eventId: eventId)
}
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didReplaceRoomWithReplacementId roomId: String) {
guard let matrixSession = self.parameters.userSessionsService.mainUserSession?.matrixSession else {
return
}
let roomCoordinatorParameters = RoomCoordinatorParameters(navigationRouterStore: NavigationRouterStore.shared,
userIndicatorPresenter: detailUserIndicatorPresenter,
session: matrixSession,
parentSpaceId: self.currentSpaceId,
roomId: roomId,
eventId: nil,
showSettingsInitially: true)
self.showRoom(with: roomCoordinatorParameters,
stackOnSplitViewDetail: false)
}
}
@@ -14,9 +14,18 @@
// limitations under the License.
//
// swiftlint:disable file_length
import UIKit
import Reusable
protocol AllChatsViewControllerDelegate: AnyObject {
func allChatsViewControllerDidCompleteAuthentication(_ allChatsViewController: AllChatsViewController)
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectRoomWithParameters roomNavigationParameters: RoomNavigationParameters, completion: @escaping () -> Void)
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectRoomPreviewWithParameters roomPreviewNavigationParameters: RoomPreviewNavigationParameters, completion: @escaping () -> Void)
func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectContact contact: MXKContact, with presentationParameters: ScreenPresentationParameters)
}
class AllChatsViewController: HomeViewController {
// MARK: - Class methods
@@ -33,6 +42,10 @@ class AllChatsViewController: HomeViewController {
return viewController
}
// MARK: - Properties
weak var allChatsDelegate: AllChatsViewControllerDelegate?
// MARK: - Private
private let searchController = UISearchController(searchResultsController: nil)
@@ -45,6 +58,40 @@ class AllChatsViewController: HomeViewController {
private var childCoordinators: [Coordinator] = []
private let tableViewPaginationThrottler = MXThrottler(minimumDelay: 0.1)
private var reviewSessionAlertHasBeenDisplayed: Bool = false
private var bannerView: UIView? {
didSet {
bannerView?.translatesAutoresizingMaskIntoConstraints = false
set(tableHeadeView: bannerView)
}
}
private var isOnboardingCoordinatorPreparing: Bool = false
private var allChatsOnboardingCoordinatorBridgePresenter: AllChatsOnboardingCoordinatorBridgePresenter?
private var currentAlert: UIAlertController?
// MARK: - SplitViewMasterViewControllerProtocol
// References on the currently selected room
private(set) var selectedRoomId: String?
private(set) var selectedEventId: String?
private(set) var selectedRoomSession: MXSession?
private(set) var selectedRoomPreviewData: RoomPreviewData?
// References on the currently selected contact
private(set) var selectedContact: MXKContact?
// Reference to the current onboarding flow. It is always nil unless the flow is being presented.
private(set) var onboardingCoordinatorBridgePresenter: OnboardingCoordinatorBridgePresenter?
// Tell whether the onboarding screen is preparing.
private(set) var isOnboardingInProgress: Bool = false
// MARK: - Lifecycle
override func viewDidLoad() {
@@ -57,9 +104,12 @@ class AllChatsViewController: HomeViewController {
recentsTableView.clipsToBounds = false
recentsTableView.register(RecentEmptySectionTableViewCell.nib, forCellReuseIdentifier: RecentEmptySectionTableViewCell.reuseIdentifier)
recentsTableView.register(RecentsInvitesTableViewCell.nib, forCellReuseIdentifier: RecentsInvitesTableViewCell.reuseIdentifier)
recentsTableView.contentInsetAdjustmentBehavior = .automatic
updateUI()
vc_setLargeTitleDisplayMode(.automatic)
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
@@ -72,12 +122,44 @@ class AllChatsViewController: HomeViewController {
self.navigationController?.isToolbarHidden = false
self.navigationController?.toolbar.tintColor = ThemeService.shared().theme.colors.accent
if self.tabBarController?.navigationItem.searchController == nil {
self.tabBarController?.navigationItem.searchController = searchController
if self.navigationItem.searchController == nil {
self.navigationItem.searchController = searchController
}
NotificationCenter.default.addObserver(self, selector: #selector(self.spaceListDidChange), name: MXSpaceService.didInitialise, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.spaceListDidChange), name: MXSpaceService.didBuildSpaceGraph, object: nil)
set(tableHeadeView: self.bannerView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Check whether we're not logged in
let authIsShown: Bool
if MXKAccountManager.shared().accounts.isEmpty {
showOnboardingFlow()
authIsShown = true
} else {
// Display a login screen if the account is soft logout
// Note: We support only one account
if let account = MXKAccountManager.shared().accounts.first, account.isSoftLogout {
showSoftLogoutOnboardingFlow(with: account.mxCredentials)
authIsShown = true
} else {
authIsShown = false
}
}
guard !authIsShown else {
return
}
AppDelegate.theDelegate().checkAppVersion()
if BuildSettings.isNewAppLayoutActivated && !RiotSettings.shared.allChatsOnboardingHasBeenDisplayed {
self.showAllChatsOnboardingScreen()
}
}
override func viewWillDisappear(_ animated: Bool) {
@@ -85,13 +167,60 @@ class AllChatsViewController: HomeViewController {
self.navigationController?.isToolbarHidden = true
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { context in
self.recentsTableView?.tableHeaderView?.layoutIfNeeded()
self.recentsTableView?.tableHeaderView = self.recentsTableView?.tableHeaderView
}
}
// MARK: - HomeViewController
// MARK: - Public
func switchSpace(withId spaceId: String?) {
searchController.isActive = false
guard let spaceId = spaceId else {
self.dataSource?.currentSpace = nil
updateUI()
return
}
guard let space = self.mainSession.spaceService.getSpace(withId: spaceId) else {
MXLog.warning("[AllChatsViewController] switchSpace: no space found with id \(spaceId)")
return
}
self.dataSource.currentSpace = space
updateUI()
self.recentsTableView.setContentOffset(.zero, animated: true)
}
override var recentsDataSourceMode: RecentsDataSourceMode {
.allChats
}
override func addMatrixSession(_ mxSession: MXSession!) {
super.addMatrixSession(mxSession)
initDataSource()
}
private func initDataSource() {
guard self.dataSource == nil, let mainSession = self.mxSessions.first as? MXSession else {
return
}
MXLog.debug("[AllChatsViewController] initDataSource")
let recentsListService = RecentsListService(withSession: mainSession)
let recentsDataSource = RecentsDataSource(matrixSession: mainSession, recentsListService: recentsListService)
displayList(recentsDataSource)
recentsDataSource?.setDelegate(self, andRecentsDataSourceMode: self.recentsDataSourceMode)
}
@objc private func spaceListDidChange() {
guard self.editActionProvider.shouldUpdate(with: self.mainSession, parentSpace: self.dataSource?.currentSpace) else {
return
@@ -118,6 +247,10 @@ class AllChatsViewController: HomeViewController {
]
}
override func startActivityIndicator() {
super.startActivityIndicator()
}
// MARK: - Actions
@objc private func showSpaceSelectorAction(sender: AnyObject) {
@@ -174,6 +307,23 @@ class AllChatsViewController: HomeViewController {
showRoomInviteList()
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
super.tableView(tableView, willDisplay: cell, forRowAt: indexPath)
guard let recentsDataSource = dataSource as? RecentsDataSource else {
return
}
let sectionType = recentsDataSource.sections.sectionType(forSectionIndex: indexPath.section)
guard sectionType == .allChats, let numberOfRowsInSection = recentsDataSource.recentsListService.allChatsRoomListData?.counts.numberOfRooms, indexPath.row == numberOfRowsInSection - 1 else {
return
}
tableViewPaginationThrottler.throttle {
recentsDataSource.paginate(inSection: indexPath.section)
}
}
// MARK: - Toolbar animation
private var lastScrollPosition: Double = 0
@@ -240,13 +390,11 @@ class AllChatsViewController: HomeViewController {
let shouldShowEmptyView = super.shouldShowEmptyView()
if shouldShowEmptyView {
self.tabBarController?.navigationItem.searchController = nil
self.navigationItem.searchController = nil
navigationItem.largeTitleDisplayMode = .never
navigationController?.navigationBar.prefersLargeTitles = false
} else {
self.tabBarController?.navigationItem.searchController = searchController
self.navigationItem.searchController = searchController
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
}
return shouldShowEmptyView
@@ -258,7 +406,7 @@ class AllChatsViewController: HomeViewController {
override func userInterfaceThemeDidChange() {
super.userInterfaceThemeDidChange()
guard self.tabBarController?.toolbarItems != nil else {
guard self.toolbarItems != nil else {
return
}
@@ -271,6 +419,17 @@ class AllChatsViewController: HomeViewController {
// MARK: - Private
private func set(tableHeadeView: UIView?) {
guard let tableView = recentsTableView else {
return
}
tableView.tableHeaderView = tableHeadeView
tableView.tableHeaderView?.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
tableView.tableHeaderView?.layoutIfNeeded()
tableView.tableHeaderView = self.recentsTableView?.tableHeaderView
}
@objc private func setupEditOptions() {
guard let currentSpace = self.dataSource?.currentSpace else {
updateRightNavigationItem(with: AllChatsActionProvider().menu)
@@ -284,7 +443,7 @@ class AllChatsViewController: HomeViewController {
private func updateUI() {
let currentSpace = self.dataSource?.currentSpace
self.tabBarController?.title = currentSpace?.summary?.displayname ?? VectorL10n.allChatsTitle
self.title = currentSpace?.summary?.displayname ?? VectorL10n.allChatsTitle
setupEditOptions()
updateToolbar(with: editActionProvider.updateMenu(with: mainSession, parentSpace: currentSpace, completion: { [weak self] menu in
@@ -294,13 +453,13 @@ class AllChatsViewController: HomeViewController {
}
private func updateRightNavigationItem(with menu: UIMenu) {
self.tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), menu: menu)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), menu: menu)
}
private func updateToolbar(with menu: UIMenu) {
self.navigationController?.isToolbarHidden = false
self.update(with: ThemeService.shared().theme)
self.tabBarController?.setToolbarItems([
self.setToolbarItems([
UIBarButtonItem(image: Asset.Images.allChatsSpacesIcon.image, style: .done, target: self, action: #selector(self.showSpaceSelectorAction(sender: ))),
UIBarButtonItem.flexibleSpace(),
UIBarButtonItem(image: Asset.Images.allChatsEditIcon.image, menu: menu)
@@ -330,27 +489,6 @@ class AllChatsViewController: HomeViewController {
coordinator.start()
}
private func switchSpace(withId spaceId: String?) {
searchController.isActive = false
guard let spaceId = spaceId else {
self.dataSource.currentSpace = nil
updateUI()
return
}
guard let space = self.mainSession.spaceService.getSpace(withId: spaceId) else {
MXLog.warning("[AllChatsViewController] switchSpace: no space found with id \(spaceId)")
return
}
self.dataSource.currentSpace = space
updateUI()
self.recentsTableView.setContentOffset(.zero, animated: true)
}
private func add(childCoordinator: Coordinator) {
self.childCoordinators.append(childCoordinator)
}
@@ -444,6 +582,21 @@ class AllChatsViewController: HomeViewController {
invitesViewController.displayList(recentsDataSource)
self.navigationController?.pushViewController(invitesViewController, animated: true)
}
private func showAllChatsOnboardingScreen() {
let allChatsOnboardingCoordinatorBridgePresenter = AllChatsOnboardingCoordinatorBridgePresenter()
allChatsOnboardingCoordinatorBridgePresenter.completion = { [weak self] in
RiotSettings.shared.allChatsOnboardingHasBeenDisplayed = true
guard let self = self else { return }
self.allChatsOnboardingCoordinatorBridgePresenter?.dismiss(animated: true, completion: {
self.allChatsOnboardingCoordinatorBridgePresenter = nil
})
}
allChatsOnboardingCoordinatorBridgePresenter.present(from: self, animated: true)
self.allChatsOnboardingCoordinatorBridgePresenter = allChatsOnboardingCoordinatorBridgePresenter
}
}
// MARK: - SpaceSelectorBottomSheetCoordinatorBridgePresenterDelegate
@@ -565,3 +718,304 @@ extension AllChatsViewController: SpaceMembersCoordinatorDelegate {
}
}
// MARK: - BannerPresentationProtocol
extension AllChatsViewController: BannerPresentationProtocol {
func presentBannerView(_ bannerView: UIView, animated: Bool) {
self.bannerView = bannerView
}
func dismissBannerView(animated: Bool) {
self.bannerView = nil
}
}
// TODO: The `MasterTabBarViewController` is called from the entire app through the `LegacyAppDelegate`. this part of the code should be moved into `AppCoordinator`
// MARK: - SplitViewMasterViewControllerProtocol
extension AllChatsViewController: SplitViewMasterViewControllerProtocol {
/// Release the current selected item (if any).
func releaseSelectedItem() {
selectedRoomId = nil
selectedEventId = nil
selectedRoomSession = nil
selectedRoomPreviewData = nil
selectedContact = nil
}
/// Refresh the missed conversations badges on tab bar icon
func refreshTabBarBadges() {
// Nothing to do here as we don't have tab bar
}
/// Verify the current device if needed.
///
/// - Parameters:
/// - session: the matrix session.
func presentVerifyCurrentSessionAlertIfNeeded(with session: MXSession) {
guard !RiotSettings.shared.hideVerifyThisSessionAlert, !reviewSessionAlertHasBeenDisplayed, !isOnboardingInProgress else {
return
}
reviewSessionAlertHasBeenDisplayed = true
// Force verification if required by the HS configuration
guard !session.vc_homeserverConfiguration().encryption.isSecureBackupRequired else {
MXLog.debug("[AllChatsViewController] presentVerifyCurrentSessionAlertIfNeededWithSession: Force verification of the device")
AppDelegate.theDelegate().presentCompleteSecurity(for: session)
return
}
presentVerifyCurrentSessionAlert(with: session)
}
/// Verify others device if needed.
///
/// - Parameters:
/// - session: the matrix session.
func presentReviewUnverifiedSessionsAlertIfNeeded(with session: MXSession) {
guard !RiotSettings.shared.hideReviewSessionsAlert, !reviewSessionAlertHasBeenDisplayed else {
return
}
let devices = mainSession.crypto.devices(forUser: mainSession.myUserId).values
var userHasOneUnverifiedDevice = false
for device in devices {
if !device.trustLevel.isCrossSigningVerified {
userHasOneUnverifiedDevice = true
break
}
}
if userHasOneUnverifiedDevice {
reviewSessionAlertHasBeenDisplayed = true
presentReviewUnverifiedSessionsAlert(with: session)
}
}
func showOnboardingFlow() {
MXLog.debug("[AllChatsViewController] showOnboardingFlow")
self.showOnboardingFlowAndResetSessionFlags(true)
}
/// Display the onboarding flow configured to log back into a soft logout session.
///
/// - Parameters:
/// - credentials: the credentials of the soft logout session.
func showSoftLogoutOnboardingFlow(with credentials: MXCredentials?) {
// This method can be called after the user chooses to clear their data as the MXSession
// is opened to call logout from. So we only set the credentials when authentication isn't
// in progress to prevent a second soft logout screen being shown.
guard self.onboardingCoordinatorBridgePresenter == nil && !self.isOnboardingCoordinatorPreparing else {
return
}
MXLog.debug("[AllChatsViewController] showAuthenticationScreenAfterSoftLogout")
AuthenticationService.shared.softLogoutCredentials = credentials
self.showOnboardingFlowAndResetSessionFlags(false)
}
/// Open the room with the provided identifier in a specific matrix session.
///
/// - Parameters:
/// - parameters: the presentation parameters that contains room information plus display information.
/// - completion: the block to execute at the end of the operation.
func selectRoom(with parameters: RoomNavigationParameters, completion: @escaping () -> Void) {
releaseSelectedItem()
selectedRoomId = parameters.roomId
selectedEventId = parameters.eventId
selectedRoomSession = parameters.mxSession
allChatsDelegate?.allChatsViewController(self, didSelectRoomWithParameters: parameters, completion: completion)
refreshSelectedControllerSelectedCellIfNeeded()
}
/// Open the RoomViewController to display the preview of a room that is unknown for the user.
/// This room can come from an email invitation link or a simple link to a room.
/// - Parameters:
/// - parameters: the presentation parameters that contains room preview information plus display information.
/// - completion: the block to execute at the end of the operation.
func selectRoomPreview(with parameters: RoomPreviewNavigationParameters, completion: @escaping () -> Void) {
releaseSelectedItem()
let roomPreviewData = parameters.previewData
selectedRoomPreviewData = roomPreviewData
selectedRoomId = roomPreviewData.roomId
selectedRoomSession = roomPreviewData.mxSession
allChatsDelegate?.allChatsViewController(self, didSelectRoomPreviewWithParameters: parameters, completion: completion)
refreshSelectedControllerSelectedCellIfNeeded()
}
/// Open a ContactDetailsViewController to display the information of the provided contact.
func select(_ contact: MXKContact) {
let presentationParameters = ScreenPresentationParameters(restoreInitialDisplay: true, stackAboveVisibleViews: false)
select(contact, with: presentationParameters)
}
/// Open a ContactDetailsViewController to display the information of the provided contact.
func select(_ contact: MXKContact, with presentationParameters: ScreenPresentationParameters) {
releaseSelectedItem()
selectedContact = contact
allChatsDelegate?.allChatsViewController(self, didSelectContact: contact, with: presentationParameters)
refreshSelectedControllerSelectedCellIfNeeded()
}
/// The current number of rooms with missed notifications, including the invites.
func missedDiscussionsCount() -> UInt {
guard let session = mxSessions as? [MXSession] else {
return 0
}
return session.reduce(0) { $0 + $1.vc_missedDiscussionsCount() }
}
/// The current number of rooms with unread highlighted messages.
func missedHighlightDiscussionsCount() -> UInt {
guard let session = mxSessions as? [MXSession] else {
return 0
}
return session.reduce(0) { $0 + $1.missedHighlightDiscussionsCount() }
}
/// Emulated `UItabBarViewController.selectedViewController` member
var selectedViewController: UIViewController? {
return self
}
var tabBar: UITabBar? {
return nil
}
// MARK: - Private
private func presentVerifyCurrentSessionAlert(with session: MXSession) {
MXLog.debug("[AllChatsViewController] presentVerifyCurrentSessionAlertWithSession")
currentAlert?.dismiss(animated: true, completion: nil)
let alert = UIAlertController(title: VectorL10n.keyVerificationSelfVerifyCurrentSessionAlertTitle,
message: VectorL10n.keyVerificationSelfVerifyCurrentSessionAlertMessage,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: VectorL10n.keyVerificationSelfVerifyCurrentSessionAlertValidateAction,
style: .default,
handler: { action in
AppDelegate.theDelegate().presentCompleteSecurity(for: session)
}))
alert.addAction(UIAlertAction(title: VectorL10n.later, style: .cancel))
alert.addAction(UIAlertAction(title: VectorL10n.doNotAskAgain,
style: .destructive,
handler: { action in
RiotSettings.shared.hideVerifyThisSessionAlert = true
}))
self.present(alert, animated: true)
currentAlert = alert
}
private func presentReviewUnverifiedSessionsAlert(with session: MXSession) {
MXLog.debug("[AllChatsViewController] presentReviewUnverifiedSessionsAlert")
currentAlert?.dismiss(animated: true, completion: nil)
let alert = UIAlertController(title: VectorL10n.keyVerificationSelfVerifyUnverifiedSessionsAlertTitle,
message: VectorL10n.keyVerificationSelfVerifyUnverifiedSessionsAlertMessage,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: VectorL10n.keyVerificationSelfVerifyUnverifiedSessionsAlertValidateAction,
style: .default,
handler: { action in
self.showSettingsSecurityScreen(with: session)
}))
alert.addAction(UIAlertAction(title: VectorL10n.later, style: .cancel))
alert.addAction(UIAlertAction(title: VectorL10n.doNotAskAgain, style: .destructive, handler: { action in
RiotSettings.shared.hideReviewSessionsAlert = true
}))
present(alert, animated: true)
currentAlert = alert
}
private func showSettingsSecurityScreen(with session: MXSession) {
guard let settingsViewController = SettingsViewController.instantiate() else {
MXLog.warning("[AllChatsViewController] showSettingsSecurityScreen: cannot instantiate SettingsViewController")
return
}
guard let securityViewController = SecurityViewController.instantiate(withMatrixSession: session) else {
MXLog.warning("[AllChatsViewController] showSettingsSecurityScreen: cannot instantiate SecurityViewController")
return
}
settingsViewController.loadViewIfNeeded()
AppDelegate.theDelegate().restoreInitialDisplay {
self.navigationController?.viewControllers = [self, settingsViewController, securityViewController]
}
}
private func showOnboardingFlowAndResetSessionFlags(_ resetSessionFlags: Bool) {
// Check whether an authentication screen is not already shown or preparing
guard self.onboardingCoordinatorBridgePresenter == nil && !self.isOnboardingCoordinatorPreparing else {
return
}
self.isOnboardingCoordinatorPreparing = true
self.isOnboardingInProgress = true
if resetSessionFlags {
resetReviewSessionsFlags()
}
AppDelegate.theDelegate().restoreInitialDisplay {
self.presentOnboardingFlow()
}
}
private func resetReviewSessionsFlags() {
reviewSessionAlertHasBeenDisplayed = false
RiotSettings.shared.hideVerifyThisSessionAlert = false
RiotSettings.shared.hideReviewSessionsAlert = false
}
private func presentOnboardingFlow() {
MXLog.debug("[AllChatsViewController] presentOnboardingFlow")
let onboardingCoordinatorBridgePresenter = OnboardingCoordinatorBridgePresenter()
onboardingCoordinatorBridgePresenter.completion = { [weak self] in
guard let self = self else { return }
self.onboardingCoordinatorBridgePresenter?.dismiss(animated: true, completion: {
self.onboardingCoordinatorBridgePresenter = nil
})
self.isOnboardingInProgress = false // Must be set before calling didCompleteAuthentication
self.allChatsDelegate?.allChatsViewControllerDidCompleteAuthentication(self)
}
onboardingCoordinatorBridgePresenter.present(from: self, animated: true)
self.onboardingCoordinatorBridgePresenter = onboardingCoordinatorBridgePresenter
self.isOnboardingCoordinatorPreparing = false
}
private func refreshSelectedControllerSelectedCellIfNeeded() {
guard splitViewController != nil else {
return
}
// Refresh selected cell without scrolling the selected cell (We suppose it's visible here)
self.refreshCurrentSelectedCell(false)
}
}
@@ -13,8 +13,6 @@
<outlet property="recentsTableView" destination="orV-HH-88x" id="lgA-2k-pXJ"/>
<outlet property="stickyHeadersBottomContainer" destination="EXH-mK-0eB" id="95Y-KP-bwF"/>
<outlet property="stickyHeadersBottomContainerHeightConstraint" destination="SNq-Js-N7s" id="vom-iM-s6W"/>
<outlet property="stickyHeadersTopContainer" destination="JJC-Bw-6sa" id="JIy-sf-4Ya"/>
<outlet property="stickyHeadersTopContainerHeightConstraint" destination="xT1-rL-nCC" id="VaK-0W-2Mi"/>
<outlet property="view" destination="iN0-l3-epB" id="NUQ-LI-M61"/>
</connections>
</placeholder>
@@ -27,14 +25,6 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableView>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JJC-Bw-6sa">
<rect key="frame" x="0.0" y="0.0" width="375" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="RecentsVCStickyHeadersTopContainer"/>
<constraints>
<constraint firstAttribute="height" id="xT1-rL-nCC"/>
</constraints>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EXH-mK-0eB">
<rect key="frame" x="0.0" y="667" width="375" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -49,14 +39,11 @@
<constraints>
<constraint firstAttribute="trailing" secondItem="orV-HH-88x" secondAttribute="trailing" id="3Np-64-AUe"/>
<constraint firstItem="4qf-KS-Fc9" firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="Bka-Zz-CEr"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="JJC-Bw-6sa" secondAttribute="bottom" id="IMR-dV-gUS"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="bottom" id="Kmg-aC-GOO"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="top" secondItem="4qf-KS-Fc9" secondAttribute="top" id="OBu-sH-mqE"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="S3i-DW-PUB"/>
<constraint firstAttribute="trailing" secondItem="EXH-mK-0eB" secondAttribute="trailing" id="bPP-yu-FTa"/>
<constraint firstItem="orV-HH-88x" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="dTn-zC-Axs"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="l5C-zt-Env"/>
<constraint firstAttribute="trailing" secondItem="JJC-Bw-6sa" secondAttribute="trailing" id="trw-3x-yjO"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="xMc-15-1wS"/>
</constraints>
<point key="canvasLocation" x="140" y="137.18140929535232"/>
</view>
@@ -95,15 +95,17 @@ class RoomInvitesViewController: RecentsViewController {
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
super.tableView(tableView, willDisplay: cell, forRowAt: indexPath)
guard tableView.numberOfSections > indexPath.section else {
return
}
let numberOfRowsInSection = tableView.numberOfRows(inSection: indexPath.section)
guard indexPath.row == numberOfRowsInSection - 1 else {
return
}
tableViewPaginationThrottler .throttle { [weak self] in
guard let self = self, tableView.numberOfSections > indexPath.section else {
return
}
let numberOfRowsInSection = tableView.numberOfRows(inSection: indexPath.section)
if indexPath.row == numberOfRowsInSection - 1 {
self.recentsDataSource?.paginate(inSection: indexPath.section)
}
self?.recentsDataSource?.paginate(inSection: indexPath.section)
}
}
@@ -13,8 +13,6 @@
<outlet property="recentsTableView" destination="orV-HH-88x" id="lgA-2k-pXJ"/>
<outlet property="stickyHeadersBottomContainer" destination="EXH-mK-0eB" id="95Y-KP-bwF"/>
<outlet property="stickyHeadersBottomContainerHeightConstraint" destination="SNq-Js-N7s" id="vom-iM-s6W"/>
<outlet property="stickyHeadersTopContainer" destination="JJC-Bw-6sa" id="JIy-sf-4Ya"/>
<outlet property="stickyHeadersTopContainerHeightConstraint" destination="xT1-rL-nCC" id="VaK-0W-2Mi"/>
<outlet property="view" destination="iN0-l3-epB" id="NUQ-LI-M61"/>
</connections>
</placeholder>
@@ -27,14 +25,6 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableView>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JJC-Bw-6sa">
<rect key="frame" x="0.0" y="0.0" width="375" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="RecentsVCStickyHeadersTopContainer"/>
<constraints>
<constraint firstAttribute="height" id="xT1-rL-nCC"/>
</constraints>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EXH-mK-0eB">
<rect key="frame" x="0.0" y="667" width="375" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -48,15 +38,12 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="orV-HH-88x" secondAttribute="trailing" id="3Np-64-AUe"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="4kK-lm-3t8"/>
<constraint firstItem="4qf-KS-Fc9" firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="Bka-Zz-CEr"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="JJC-Bw-6sa" secondAttribute="bottom" id="IMR-dV-gUS"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="bottom" id="Kmg-aC-GOO"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="top" secondItem="4qf-KS-Fc9" secondAttribute="top" id="OBu-sH-mqE"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="S3i-DW-PUB"/>
<constraint firstAttribute="trailing" secondItem="EXH-mK-0eB" secondAttribute="trailing" id="bPP-yu-FTa"/>
<constraint firstItem="orV-HH-88x" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="dTn-zC-Axs"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="l5C-zt-Env"/>
<constraint firstAttribute="trailing" secondItem="JJC-Bw-6sa" secondAttribute="trailing" id="trw-3x-yjO"/>
</constraints>
<point key="canvasLocation" x="140" y="137.18140929535232"/>
</view>
+19 -44
View File
@@ -50,7 +50,6 @@
@property (nonatomic, strong) MXThrottler *collectionViewPaginationThrottler;
@property(nonatomic) SpaceMembersCoordinatorBridgePresenter *spaceMembersCoordinatorBridgePresenter;
@property (nonatomic, strong) MXThrottler *tableViewPaginationThrottler;
@end
@@ -73,7 +72,6 @@
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenHome];
self.collectionViewPaginationThrottler = [[MXThrottler alloc] initWithMinimumDelay:0.1];
self.tableViewPaginationThrottler = [[MXThrottler alloc] initWithMinimumDelay:0.1];
}
- (void)viewDidLoad
@@ -109,9 +107,12 @@
{
[super viewWillAppear:animated];
[ThemeService.shared.theme applyStyleOnNavigationBar:[AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar];
if (!BuildSettings.isNewAppLayoutActivated)
{
[ThemeService.shared.theme applyStyleOnNavigationBar:[AppDelegate theDelegate].masterTabBarController.navigationController.navigationBar];
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
}
if (recentsDataSource.recentsDataSourceMode != self.recentsDataSourceMode)
{
@@ -585,34 +586,6 @@
}
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
RecentsDataSourceSectionType sectionType = [recentsDataSource.sections sectionTypeForSectionIndex:indexPath.section];
if (sectionType != RecentsDataSourceSectionTypeAllChats)
{
return;
}
if ([super respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)])
{
[super tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
}
[self.tableViewPaginationThrottler throttle:^{
NSInteger section = indexPath.section;
if (tableView.numberOfSections <= section)
{
return;
}
NSInteger numberOfRowsInSection = [tableView numberOfRowsInSection:section];
if (indexPath.row == numberOfRowsInSection - 1)
{
[self->recentsDataSource paginateInSection:section];
}
}];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
@@ -691,19 +664,21 @@
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger collectionViewSection = indexPath.section;
if (collectionView.numberOfSections <= collectionViewSection)
{
return;
}
NSInteger numberOfItemsInSection = [collectionView numberOfItemsInSection:collectionViewSection];
if (indexPath.item != numberOfItemsInSection - 1)
{
return;
}
[self.collectionViewPaginationThrottler throttle:^{
NSInteger collectionViewSection = indexPath.section;
if (collectionView.numberOfSections <= collectionViewSection)
{
return;
}
NSInteger numberOfItemsInSection = [collectionView numberOfItemsInSection:collectionViewSection];
if (indexPath.item == numberOfItemsInSection - 1)
{
NSInteger tableViewSection = collectionView.tag;
[self->recentsDataSource paginateInSection:tableViewSection];
}
NSInteger tableViewSection = collectionView.tag;
[self->recentsDataSource paginateInSection:tableViewSection];
}];
}
@@ -1,26 +0,0 @@
//
// 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
class AllChatsViewControllerWithBannerWrapperViewController: HomeViewControllerWithBannerWrapperViewController {
// MARK: - MasterTabBarItemDisplayProtocol
override var masterTabBarItemTitle: String {
return VectorL10n.allChatsTitle
}
}
@@ -1,46 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VersionCheckBannerView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="479" height="150"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VersionCheckBannerView" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="479" height="109"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="5ts-Up-IFL">
<rect key="frame" x="12" y="16" width="419" height="118"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aWS-bd-dO7">
<rect key="frame" x="0.0" y="0.0" width="45" height="24"/>
<state key="normal" image="version_check_info_icon"/>
</button>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="OJ5-KQ-ePa">
<rect key="frame" x="53" y="0.0" width="366" height="75.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Were ending support for iOS 11" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="XGd-6U-k3l">
<rect key="frame" x="0.0" y="0.0" width="366" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<color key="textColor" red="0.090196078431372548" green="0.098039215686274508" blue="0.10980392156862745" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="btO-Jc-DmU">
<rect key="frame" x="0.0" y="28.5" width="366" height="47"/>
<string key="text">We will soon be ending support for Element on iOS 11. To continue using Element to its full potential, we advise you to upgrade your version of iOS.</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.090196078431372548" green="0.098039215686274508" blue="0.10980392156862745" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
</stackView>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aWS-bd-dO7">
<rect key="frame" x="12" y="16" width="24" height="24"/>
<state key="normal" image="version_check_info_icon"/>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="Were ending support for iOS 11" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="XGd-6U-k3l">
<rect key="frame" x="44" y="16" width="387" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<color key="textColor" red="0.090196078431372548" green="0.098039215686274508" blue="0.10980392156862745" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qyW-5Q-Bkm">
<rect key="frame" x="431" y="4" width="44" height="44"/>
<constraints>
@@ -52,16 +35,28 @@
<action selector="onDismissButtonTap:" destination="iN0-l3-epB" eventType="touchUpInside" id="pDN-9q-Lr5"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="999" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="btO-Jc-DmU">
<rect key="frame" x="44" y="44.5" width="387" height="48.5"/>
<string key="text">We will soon be ending support for Element on iOS 11. To continue using Element to its full potential, we advise you to upgrade your version of iOS.</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.090196078431372548" green="0.098039215686274508" blue="0.10980392156862745" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="qyW-5Q-Bkm" secondAttribute="trailing" constant="4" id="Psr-tv-GLd"/>
<constraint firstItem="aWS-bd-dO7" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="16" id="0xI-gt-7X9"/>
<constraint firstItem="btO-Jc-DmU" firstAttribute="trailing" secondItem="XGd-6U-k3l" secondAttribute="trailing" id="H2o-ii-jtJ"/>
<constraint firstItem="qyW-5Q-Bkm" firstAttribute="leading" secondItem="XGd-6U-k3l" secondAttribute="trailing" id="OlL-vw-PUW"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="qyW-5Q-Bkm" secondAttribute="trailing" constant="4" id="Psr-tv-GLd"/>
<constraint firstAttribute="bottom" secondItem="btO-Jc-DmU" secondAttribute="bottom" constant="16" id="VuJ-pJ-dHR"/>
<constraint firstItem="qyW-5Q-Bkm" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="acO-Ae-IEB"/>
<constraint firstItem="5ts-Up-IFL" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="16" id="bRM-rK-xBC"/>
<constraint firstItem="5ts-Up-IFL" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="12" id="er6-CV-ezV"/>
<constraint firstItem="qyW-5Q-Bkm" firstAttribute="leading" secondItem="5ts-Up-IFL" secondAttribute="trailing" id="rYR-NR-y0G"/>
<constraint firstAttribute="bottom" secondItem="5ts-Up-IFL" secondAttribute="bottom" priority="100" constant="16" id="rjk-yN-Qxr"/>
<constraint firstItem="XGd-6U-k3l" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="16" id="cqD-st-9bH"/>
<constraint firstItem="XGd-6U-k3l" firstAttribute="leading" secondItem="aWS-bd-dO7" secondAttribute="trailing" constant="8" id="dWv-Xb-duJ"/>
<constraint firstItem="btO-Jc-DmU" firstAttribute="leading" secondItem="XGd-6U-k3l" secondAttribute="leading" id="hgN-2M-nj7"/>
<constraint firstItem="btO-Jc-DmU" firstAttribute="top" secondItem="XGd-6U-k3l" secondAttribute="bottom" constant="8" id="hoU-ki-h6S"/>
<constraint firstItem="aWS-bd-dO7" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="12" id="vbZ-Tj-iXY"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
@@ -72,7 +67,7 @@
<outlet property="subtitleLabel" destination="btO-Jc-DmU" id="cLh-sg-vfz"/>
<outlet property="titleLabel" destination="XGd-6U-k3l" id="FTQ-fK-u4d"/>
</connections>
<point key="canvasLocation" x="277.536231884058" y="-150"/>
<point key="canvasLocation" x="261.59420289855075" y="-130.91517857142856"/>
</view>
</objects>
<resources>
+5 -5
View File
@@ -221,6 +221,11 @@
{
[super viewDidLoad];
if (BuildSettings.isNewAppLayoutActivated)
{
[self vc_setLargeTitleDisplayMode: UINavigationItemLargeTitleDisplayModeNever];
}
// Check whether the view controller has been pushed via storyboard
if (!_bubblesTableView)
{
@@ -340,11 +345,6 @@
{
_bubblesTableView.hidden = NO;
}
if (BuildSettings.isNewAppLayoutActivated)
{
[self vc_setLargeTitleDisplayMode: UINavigationItemLargeTitleDisplayModeNever];
}
}
- (void)viewDidAppear:(BOOL)animated
+2
View File
@@ -546,6 +546,8 @@ static CGSize kThreadListBarButtonItemImageSize;
}
[self updateTopBanners];
self.bubblesTableView.clipsToBounds = NO;
}
- (void)viewWillDisappear:(BOOL)animated
@@ -56,7 +56,7 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
return self.masterPresentable?.selectedNavigationRouter
}
private weak var tabBarCoordinator: TabBarCoordinatorType?
private weak var masterCoordinator: SplitViewMasterCoordinatorProtocol?
// Indicate if coordinator has been started once
private var hasStartedOnce: Bool = false
@@ -93,30 +93,29 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
self.splitViewController.delegate = self
// Create primary controller
let tabBarCoordinator = self.createTabBarCoordinator()
tabBarCoordinator.delegate = self
tabBarCoordinator.splitViewMasterPresentableDelegate = self
tabBarCoordinator.start(with: spaceId)
let masterCoordinator: SplitViewMasterCoordinatorProtocol = BuildSettings.isNewAppLayoutActivated ? self.createAllChatsCoordinator() : self.createTabBarCoordinator()
masterCoordinator.splitViewMasterPresentableDelegate = self
masterCoordinator.start(with: spaceId)
// Create secondary controller
let placeholderDetailViewController = self.createPlaceholderDetailsViewController()
let detailNavigationController = RiotNavigationController(rootViewController: placeholderDetailViewController)
// Setup split view controller
self.splitViewController.viewControllers = [tabBarCoordinator.toPresentable(), detailNavigationController]
self.splitViewController.viewControllers = [masterCoordinator.toPresentable(), detailNavigationController]
// Setup detail user indicator presenter
let context = SplitViewUserIndicatorPresentationContext(
splitViewController: splitViewController,
tabBarCoordinator: tabBarCoordinator,
masterCoordinator: masterCoordinator,
detailNavigationController: detailNavigationController
)
detailUserIndicatorPresenter = UserIndicatorTypePresenter(presentationContext: context)
self.add(childCoordinator: tabBarCoordinator)
self.add(childCoordinator: masterCoordinator)
self.tabBarCoordinator = tabBarCoordinator
self.masterPresentable = tabBarCoordinator
self.masterCoordinator = masterCoordinator
self.masterPresentable = masterCoordinator
self.detailNavigationController = detailNavigationController
self.detailNavigationRouter = NavigationRouter(navigationController: detailNavigationController)
@@ -127,7 +126,7 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
// Pop to home screen when selecting a new space
self.popToHome(animated: true) {
// Update tabBarCoordinator selected space
self.tabBarCoordinator?.start(with: spaceId)
self.masterCoordinator?.start(with: spaceId)
}
}
}
@@ -146,26 +145,26 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
self.resetDetailNavigationController(animated: animated)
// Release the current selected item (room/contact/group...).
self.tabBarCoordinator?.releaseSelectedItems()
self.masterCoordinator?.releaseSelectedItems()
}
func popToHome(animated: Bool, completion: (() -> Void)?) {
self.resetDetails(animated: animated)
// Force back to the main screen if this is not the one that is displayed
self.tabBarCoordinator?.popToHome(animated: animated, completion: completion)
self.masterCoordinator?.popToHome(animated: animated, completion: completion)
}
func showErroIndicator(with error: Error) {
tabBarCoordinator?.showErroIndicator(with: error)
masterCoordinator?.showErroIndicator(with: error)
}
func hideAppStateIndicator() {
tabBarCoordinator?.hideAppStateIndicator()
masterCoordinator?.hideAppStateIndicator()
}
func showAppStateIndicator(with text: String, icon: UIImage?) {
tabBarCoordinator?.showAppStateIndicator(with: text, icon: icon)
masterCoordinator?.showAppStateIndicator(with: text, icon: icon)
}
// MARK: - Private methods
@@ -174,6 +173,14 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
return PlaceholderDetailViewController.instantiate()
}
private func createAllChatsCoordinator() -> AllChatsCoordinator {
let coordinatorParameters = AllChatsCoordinatorParameters(userSessionsService: self.parameters.userSessionsService, appNavigator: self.parameters.appNavigator)
let coordinator = AllChatsCoordinator(parameters: coordinatorParameters)
coordinator.delegate = self
return coordinator
}
private func createTabBarCoordinator() -> TabBarCoordinator {
let coordinatorParameters = TabBarCoordinatorParameters(userSessionsService: self.parameters.userSessionsService, appNavigator: self.parameters.appNavigator)
@@ -340,8 +347,8 @@ extension SplitViewCoordinator: UISplitViewControllerDelegate {
}
// MARK: - TabBarCoordinatorDelegate
extension SplitViewCoordinator: TabBarCoordinatorDelegate {
func tabBarCoordinatorDidCompleteAuthentication(_ coordinator: TabBarCoordinatorType) {
extension SplitViewCoordinator: SplitViewMasterCoordinatorDelegate {
func splitViewMasterCoordinatorDidCompleteAuthentication(_ coordinator: SplitViewMasterCoordinatorProtocol) {
self.delegate?.splitViewCoordinatorDidCompleteAuthentication(self)
}
}
@@ -0,0 +1,112 @@
//
// 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 SplitViewMasterViewControllerProtocol_h
#define SplitViewMasterViewControllerProtocol_h
@class RoomNavigationParameters;
@class RoomPreviewNavigationParameters;
@class ScreenPresentationParameters;
@class OnboardingCoordinatorBridgePresenter;
@protocol SplitViewMasterTabBarViewControllerProtocol <NSObject>
/// Refresh the missed conversations badges on tab bar icon
- (void)refreshTabBarBadges;
/// Emulated `UITabBarViewController.selectedViewController` property
@property (nonatomic, readonly, nullable) UIViewController *selectedViewController;
/// Emulated `UITabBarViewController.tabBar` property
@property (nonatomic, readonly, nullable) UITabBar *tabBar;
@end
/// `SplitViewMasterViewControllerProtocol` describe the methods and properties needed by
@protocol SplitViewMasterViewControllerProtocol <SplitViewMasterTabBarViewControllerProtocol>
/// Display the default onboarding flow.
- (void)showOnboardingFlow;
/// Display the onboarding flow configured to log back into a soft logout session.
///
/// @param softLogoutCredentials the credentials of the soft logout session.
- (void)showSoftLogoutOnboardingFlowWithCredentials:(MXCredentials*)softLogoutCredentials;
/// Open the room with the provided identifier in a specific matrix session.
///
/// @param parameters the presentation parameters that contains room information plus display information.
/// @param completion the block to execute at the end of the operation.
- (void)selectRoomWithParameters:(RoomNavigationParameters*)parameters completion:(void (^)(void))completion;
/// Open the RoomViewController to display the preview of a room that is unknown for the user.
/// This room can come from an email invitation link or a simple link to a room.
///
/// @param parameters the presentation parameters that contains room preview information plus display information.
/// @param completion the block to execute at the end of the operation.
- (void)selectRoomPreviewWithParameters:(RoomPreviewNavigationParameters*)parameters completion:(void (^)(void))completion;
/// Open a ContactDetailsViewController to display the information of the provided contact.
///
/// @param contact contact to be displayed
- (void)selectContact:(MXKContact*)contact;
/// Open a ContactDetailsViewController to display the information of the provided contact according to the presentation parameters.
///
/// @param contact contact to be displayed
/// @param presentationParameters the presentation parameters that contains room preview information plus display information.
- (void)selectContact:(MXKContact*)contact withPresentationParameters:(ScreenPresentationParameters*)presentationParameters;
/// Release the current selected item (if any).
- (void)releaseSelectedItem;
/// The current number of rooms with missed notifications, including the invites.
- (NSUInteger)missedDiscussionsCount;
/// The current number of rooms with unread highlighted messages.
- (NSUInteger)missedHighlightDiscussionsCount;
/// Verify the current device if needed.
///
/// @param session the matrix session.
- (void)presentVerifyCurrentSessionAlertIfNeededWithSession:(MXSession*)session;
/// Verify others device if needed.
///
/// @param session the matrix session.
- (void)presentReviewUnverifiedSessionsAlertIfNeededWithSession:(MXSession*)session;
/// Reference to the current onboarding flow. It is always nil unless the flow is being presented.
@property (nonatomic, readonly) OnboardingCoordinatorBridgePresenter *onboardingCoordinatorBridgePresenter;
/// Reference on the currently selected room
@property (nonatomic, readonly) NSString *selectedRoomId;
/// Reference on the currently selected event
@property (nonatomic, readonly) NSString *selectedEventId;
/// Reference on the currently selected room session
@property (nonatomic, readonly) MXSession *selectedRoomSession;
/// Reference on the currently selected room preview data
@property (nonatomic, readonly) RoomPreviewData *selectedRoomPreviewData;
/// Reference on the currently selected contact
@property (nonatomic, readonly) MXKContact *selectedContact;
/// `true` while the onboarding flow is displayed
@property (nonatomic, readonly) BOOL isOnboardingInProgress;
@end
#endif /* SplitViewMasterViewControllerProtocol_h */
@@ -19,28 +19,28 @@ import CommonKit
class SplitViewUserIndicatorPresentationContext: UserIndicatorPresentationContext {
private weak var splitViewController: UISplitViewController?
private weak var tabBarCoordinator: TabBarCoordinator?
private weak var masterCoordinator: SplitViewMasterCoordinatorProtocol?
private weak var detailNavigationController: UINavigationController?
init(
splitViewController: UISplitViewController,
tabBarCoordinator: TabBarCoordinator,
masterCoordinator: SplitViewMasterCoordinatorProtocol,
detailNavigationController: UINavigationController
) {
self.splitViewController = splitViewController
self.tabBarCoordinator = tabBarCoordinator
self.masterCoordinator = masterCoordinator
self.detailNavigationController = detailNavigationController
}
var indicatorPresentingViewController: UIViewController? {
guard
let splitViewController = splitViewController,
let tabBarCoordinator = tabBarCoordinator,
let masterCoordinator = masterCoordinator,
let detailNavigationController = detailNavigationController
else {
MXLog.debug("[SplitViewCoordinator]: Missing tab bar or detail coordinator, cannot update user indicator presenter")
return nil
}
return splitViewController.isCollapsed ? tabBarCoordinator.toPresentable() : detailNavigationController
return splitViewController.isCollapsed ? masterCoordinator.toPresentable() : detailNavigationController
}
}
+2 -82
View File
@@ -22,6 +22,7 @@
#import "FavouritesViewController.h"
#import "PeopleViewController.h"
#import "RoomsViewController.h"
#import "SplitViewMasterViewControllerProtocol.h"
#define TABBAR_HOME_INDEX 0
#define TABBAR_FAVOURITES_INDEX 1
@@ -42,7 +43,7 @@ typedef NS_ENUM(NSUInteger, MasterTabBarIndex) {
@class ScreenPresentationParameters;
@class OnboardingCoordinatorBridgePresenter;
@interface MasterTabBarController : UITabBarController
@interface MasterTabBarController : UITabBarController<SplitViewMasterViewControllerProtocol>
// UITabBarController already have a `delegate` property
@property (weak, nonatomic) id<MasterTabBarControllerDelegate> masterTabBarDelegate;
@@ -55,71 +56,6 @@ typedef NS_ENUM(NSUInteger, MasterTabBarIndex) {
// Remove a matrix session.
- (void)removeMatrixSession:(MXSession*)mxSession;
/**
Display the default onboarding flow.
*/
- (void)showOnboardingFlow;
/**
Display the onboarding flow configured to log back into a soft logout session.
@param softLogoutCredentials the credentials of the soft logout session.
*/
- (void)showSoftLogoutOnboardingFlowWithCredentials:(MXCredentials*)softLogoutCredentials;
/// Open the room with the provided identifier in a specific matrix session.
/// @param parameters the presentation parameters that contains room information plus display information.
/// @param completion the block to execute at the end of the operation.
- (void)selectRoomWithParameters:(RoomNavigationParameters*)parameters completion:(void (^)(void))completion;
/// Open the RoomViewController to display the preview of a room that is unknown for the user.
/// This room can come from an email invitation link or a simple link to a room.
/// @param parameters the presentation parameters that contains room preview information plus display information.
/// @param completion the block to execute at the end of the operation.
- (void)selectRoomPreviewWithParameters:(RoomPreviewNavigationParameters*)parameters completion:(void (^)(void))completion;
/**
Open a ContactDetailsViewController to display the information of the provided contact.
*/
- (void)selectContact:(MXKContact*)contact;
- (void)selectContact:(MXKContact*)contact withPresentationParameters:(ScreenPresentationParameters*)presentationParameters;
/**
Release the current selected item (if any).
*/
- (void)releaseSelectedItem;
/**
The current number of rooms with missed notifications, including the invites.
*/
- (NSUInteger)missedDiscussionsCount;
/**
The current number of rooms with unread highlighted messages.
*/
- (NSUInteger)missedHighlightDiscussionsCount;
/**
Refresh the missed conversations badges on tab bar icon
*/
- (void)refreshTabBarBadges;
/**
Verify the current device if needed.
@param session the matrix session.
*/
- (void)presentVerifyCurrentSessionAlertIfNeededWithSession:(MXSession*)session;
/**
Verify others device if needed.
@param session the matrix session.
*/
- (void)presentReviewUnverifiedSessionsAlertIfNeededWithSession:(MXSession*)session;
/// Filter rooms for each tab data source with the given room parent id.
/// It should keep rooms having an ancestor with `roomParentId` as parent id.
/// @param roomParentId The room parent id used to filter rooms.
@@ -127,27 +63,11 @@ typedef NS_ENUM(NSUInteger, MasterTabBarIndex) {
- (void)filterRoomsWithParentId:(NSString*)roomParentId
inMatrixSession:(MXSession*)mxSession;
// Reference to the current onboarding flow. It is always nil unless the flow is being presented.
@property (nonatomic, readonly) OnboardingCoordinatorBridgePresenter *onboardingCoordinatorBridgePresenter;
@property (nonatomic, readonly) HomeViewController *homeViewController;
@property (nonatomic, readonly) FavouritesViewController *favouritesViewController;
@property (nonatomic, readonly) PeopleViewController *peopleViewController;
@property (nonatomic, readonly) RoomsViewController *roomsViewController;
// References on the currently selected room
@property (nonatomic, readonly) NSString *selectedRoomId;
@property (nonatomic, readonly) NSString *selectedEventId;
@property (nonatomic, readonly) MXSession *selectedRoomSession;
@property (nonatomic, readonly) RoomPreviewData *selectedRoomPreviewData;
// References on the currently selected contact
@property (nonatomic, readonly) MXKContact *selectedContact;
// YES while the onboarding flow is displayed
@property (nonatomic, readonly) BOOL isOnboardingInProgress;
// Set tab bar item controllers
- (void)updateViewControllers:(NSArray<UIViewController*>*)viewControllers;
+13 -12
View File
@@ -65,6 +65,7 @@
@end
@implementation MasterTabBarController
@synthesize onboardingCoordinatorBridgePresenter, selectedRoomId, selectedEventId, selectedRoomSession, selectedRoomPreviewData, selectedContact, isOnboardingInProgress;
#pragma mark - Properties override
@@ -530,9 +531,9 @@
{
[self releaseSelectedItem];
_selectedRoomId = paramaters.roomId;
_selectedEventId = paramaters.eventId;
_selectedRoomSession = paramaters.mxSession;
selectedRoomId = paramaters.roomId;
selectedEventId = paramaters.eventId;
selectedRoomSession = paramaters.mxSession;
[self.masterTabBarDelegate masterTabBarController:self didSelectRoomWithParameters:paramaters completion:completion];
@@ -545,9 +546,9 @@
RoomPreviewData *roomPreviewData = parameters.previewData;
_selectedRoomPreviewData = roomPreviewData;
_selectedRoomId = roomPreviewData.roomId;
_selectedRoomSession = roomPreviewData.mxSession;
selectedRoomPreviewData = roomPreviewData;
selectedRoomId = roomPreviewData.roomId;
selectedRoomSession = roomPreviewData.mxSession;
[self.masterTabBarDelegate masterTabBarController:self didSelectRoomPreviewWithParameters:parameters completion:completion];
@@ -565,7 +566,7 @@
{
[self releaseSelectedItem];
_selectedContact = contact;
selectedContact = contact;
[self.masterTabBarDelegate masterTabBarController:self didSelectContact:contact withPresentationParameters:presentationParameters];
@@ -574,12 +575,12 @@
- (void)releaseSelectedItem
{
_selectedRoomId = nil;
_selectedEventId = nil;
_selectedRoomSession = nil;
_selectedRoomPreviewData = nil;
selectedRoomId = nil;
selectedEventId = nil;
selectedRoomSession = nil;
selectedRoomPreviewData = nil;
_selectedContact = nil;
selectedContact = nil;
}
- (NSUInteger)missedDiscussionsCount
@@ -18,15 +18,15 @@
import Foundation
protocol TabBarCoordinatorDelegate: AnyObject {
// TODO: Remove this method, authentication should not be handled by TabBarCoordinator
func tabBarCoordinatorDidCompleteAuthentication(_ coordinator: TabBarCoordinatorType)
protocol SplitViewMasterCoordinatorDelegate: AnyObject {
// TODO: Remove this method, authentication should not be handled by SplitViewMasterCoordinator
func splitViewMasterCoordinatorDidCompleteAuthentication(_ coordinator: SplitViewMasterCoordinatorProtocol)
}
/// `TabBarCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
protocol TabBarCoordinatorType: Coordinator, SplitViewMasterPresentable {
/// `SplitViewMasterCoordinatorProtocol` is a protocol describing a Coordinator that handle the master view controller of the `UISplitViewController`
protocol SplitViewMasterCoordinatorProtocol: Coordinator, SplitViewMasterPresentable {
var delegate: TabBarCoordinatorDelegate? { get }
var delegate: SplitViewMasterCoordinatorDelegate? { get }
/// Start coordinator by selecting a Space.
/// - Parameter spaceId: The id of the Space to use.
+4 -15
View File
@@ -23,7 +23,7 @@ import CommonKit
import MatrixSDK
@objcMembers
final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
final class TabBarCoordinator: NSObject, SplitViewMasterCoordinatorProtocol {
// MARK: - Properties
@@ -77,7 +77,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: TabBarCoordinatorDelegate?
weak var delegate: SplitViewMasterCoordinatorDelegate?
weak var splitViewMasterPresentableDelegate: SplitViewMasterPresentableDelegate?
@@ -297,17 +297,6 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
return versionCheckCoordinator
}
private func createAllChatsViewController() -> AllChatsViewControllerWithBannerWrapperViewController {
let allChatsViewController = AllChatsViewController.instantiate()
allChatsViewController.tabBarItem.tag = Int(TABBAR_HOME_INDEX)
allChatsViewController.tabBarItem.image = allChatsViewController.tabBarItem.image
allChatsViewController.accessibilityLabel = VectorL10n.allChatsTitle
allChatsViewController.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
let wrapperViewController = AllChatsViewControllerWithBannerWrapperViewController(viewController: allChatsViewController)
return wrapperViewController
}
private func createHomeViewController() -> HomeViewControllerWithBannerWrapperViewController {
let homeViewController: HomeViewController = HomeViewController.instantiate()
homeViewController.tabBarItem.tag = Int(TABBAR_HOME_INDEX)
@@ -378,7 +367,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
private func updateTabControllers(for tabBarController: MasterTabBarController, showCommunities: Bool) {
var viewControllers: [UIViewController] = []
let homeViewController = BuildSettings.isNewAppLayoutActivated ? self.createAllChatsViewController() : self.createHomeViewController()
let homeViewController = self.createHomeViewController()
viewControllers.append(homeViewController)
if !BuildSettings.isNewAppLayoutActivated {
@@ -944,7 +933,7 @@ extension TabBarCoordinator: MasterTabBarControllerDelegate {
}
func masterTabBarControllerDidCompleteAuthentication(_ masterTabBarController: MasterTabBarController!) {
self.delegate?.tabBarCoordinatorDidCompleteAuthentication(self)
self.delegate?.splitViewMasterCoordinatorDidCompleteAuthentication(self)
}
func masterTabBarController(_ masterTabBarController: MasterTabBarController!, didSelectRoomWithId roomId: String!, andEventId eventId: String!, inMatrixSession matrixSession: MXSession!, completion: (() -> Void)!) {
@@ -70,3 +70,4 @@
#import "UIAlertController+MatrixKit.h"
#import "MXKMessageTextView.h"
#import "AllChatsLayoutModels.h"
#import "SecurityViewController.h"
+1
View File
@@ -0,0 +1 @@
Glitchy room list header when scrolling
+1
View File
@@ -0,0 +1 @@
Performance issues with new App Layout