mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-25 02:52:45 +02:00
Add RoomCoordinator.
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Room Room
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: RoomCoordinatorParameters
|
||||
private let roomViewController: RoomViewController
|
||||
private let activityIndicatorPresenter: ActivityIndicatorPresenterType
|
||||
private var selectedEventId: String?
|
||||
|
||||
private var roomDataSourceManager: MXKRoomDataSourceManager {
|
||||
return MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.parameters.session)
|
||||
}
|
||||
|
||||
/// Indicate true if the Coordinator has started once
|
||||
private var hasStartedOnce: Bool {
|
||||
return self.roomViewController.delegate != nil
|
||||
}
|
||||
|
||||
private var navigationRouter: NavigationRouterType? {
|
||||
|
||||
var finalNavigationRouter: NavigationRouterType?
|
||||
|
||||
if let navigationRouter = self.parameters.navigationRouter {
|
||||
finalNavigationRouter = navigationRouter
|
||||
} else if let navigationRouterStore = self.parameters.navigationRouterStore, let currentNavigationController = self.roomViewController.navigationController {
|
||||
// If no navigationRouter has been provided, try to get the navigation router from the current RoomViewController navigation controller if exists
|
||||
finalNavigationRouter = navigationRouterStore.getOrCreateNavigationRouter(for: currentNavigationController)
|
||||
}
|
||||
|
||||
return finalNavigationRouter
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: RoomCoordinatorDelegate?
|
||||
|
||||
var canReleaseRoomDataSource: Bool {
|
||||
// If the displayed data is not a preview, let the manager release the room data source
|
||||
// (except if the view controller has the room data source ownership).
|
||||
return self.parameters.previewData == nil && self.roomViewController.roomDataSource != nil && self.roomViewController.hasRoomDataSourceOwnership == false
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: RoomCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
self.selectedEventId = parameters.eventId
|
||||
|
||||
self.roomViewController = RoomViewController.instantiate()
|
||||
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
|
||||
self.roomViewController.delegate = self
|
||||
|
||||
// Detect when view controller has been dismissed by gesture when presented modally (not in full screen).
|
||||
self.roomViewController.presentationController?.delegate = self
|
||||
|
||||
if let eventId = self.selectedEventId {
|
||||
self.start(with: self.parameters.roomId, and: eventId)
|
||||
} else {
|
||||
self.start(with: self.parameters.roomId)
|
||||
}
|
||||
|
||||
// Add `roomViewController` to the NavigationRouter, only if it has been explicity set as parameter
|
||||
if let navigationRouter = self.parameters.navigationRouter {
|
||||
if navigationRouter.modules.isEmpty == false {
|
||||
navigationRouter.push(self.roomViewController, animated: true, popCompletion: nil)
|
||||
} else {
|
||||
navigationRouter.setRootModule(self.roomViewController, popCompletion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this method when the room screen is already shown and you want to go to a specific event.
|
||||
/// i.e User tap on push notification message for the current displayed room
|
||||
func start(withEventId eventId: String) {
|
||||
|
||||
self.selectedEventId = eventId
|
||||
|
||||
if self.hasStartedOnce {
|
||||
self.start(with: self.parameters.roomId, and: eventId)
|
||||
} else {
|
||||
self.start()
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.roomViewController
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func start(with roomId: String) {
|
||||
|
||||
// Present activity indicator when retrieving roomDataSource for given room ID
|
||||
self.activityIndicatorPresenter.presentActivityIndicator(on: roomViewController.view, animated: false)
|
||||
|
||||
let roomDataSourceManager: MXKRoomDataSourceManager = MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.parameters.session)
|
||||
|
||||
// LIVE: Show the room live timeline managed by MXKRoomDataSourceManager
|
||||
roomDataSourceManager.roomDataSource(forRoom: roomId, create: true, onComplete: { [weak self] (roomDataSource) in
|
||||
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
if let roomDataSource = roomDataSource {
|
||||
self.roomViewController.displayRoom(roomDataSource)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func start(with roomId: String, and eventId: String) {
|
||||
|
||||
// Present activity indicator when retrieving roomDataSource for given room ID
|
||||
self.activityIndicatorPresenter.presentActivityIndicator(on: roomViewController.view, animated: false)
|
||||
|
||||
// Open the room on the requested event
|
||||
RoomDataSource.load(withRoomId: roomId,
|
||||
initialEventId: eventId,
|
||||
andMatrixSession: self.parameters.session) { [weak self] (dataSource) in
|
||||
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
guard let roomDataSource = dataSource as? RoomDataSource else {
|
||||
return
|
||||
}
|
||||
|
||||
roomDataSource.markTimelineInitialEvent = true
|
||||
self.roomViewController.displayRoom(roomDataSource)
|
||||
|
||||
// Give the data source ownership to the room view controller.
|
||||
self.roomViewController.hasRoomDataSourceOwnership = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomIdentifiable
|
||||
extension RoomCoordinator: RoomIdentifiable {
|
||||
|
||||
var roomId: String? {
|
||||
return self.parameters.roomId
|
||||
}
|
||||
|
||||
var mxSession: MXSession? {
|
||||
self.parameters.session
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
|
||||
extension RoomCoordinator: UIAdaptivePresentationControllerDelegate {
|
||||
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
self.delegate?.roomCoordinatorDidDismissInteractively(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomViewControllerDelegate
|
||||
extension RoomCoordinator: RoomViewControllerDelegate {
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, showRoomWithId roomID: String) {
|
||||
self.delegate?.roomCoordinator(self, didSelectRoomWithId: roomID)
|
||||
}
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, showMemberDetails roomMember: MXRoomMember) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
func roomViewControllerShowRoomDetails(_ roomViewController: RoomViewController) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
func roomViewControllerDidLeaveRoom(_ roomViewController: RoomViewController) {
|
||||
self.delegate?.roomCoordinatorDidLeaveRoom(self)
|
||||
}
|
||||
|
||||
func roomViewControllerPreviewDidTapCancel(_ roomViewController: RoomViewController) {
|
||||
self.delegate?.roomCoordinatorDidCancelRoomPreview(self)
|
||||
}
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, startChatWithUserId userId: String, completion: @escaping () -> Void) {
|
||||
AppDelegate.theDelegate().createDirectChat(withUserId: userId, completion: completion)
|
||||
}
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, showCompleteSecurityFor session: MXSession) {
|
||||
AppDelegate.theDelegate().presentCompleteSecurity(for: session)
|
||||
}
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, handleUniversalLinkFragment fragment: String, from universalLinkURL: URL?) -> Bool {
|
||||
return AppDelegate.theDelegate().handleUniversalLinkFragment(fragment, from: universalLinkURL)
|
||||
}
|
||||
|
||||
func roomViewController(_ roomViewController: RoomViewController, handleUniversalLinkURL universalLinkURL: URL) -> Bool {
|
||||
return AppDelegate.theDelegate().handleUniversalLinkURL(universalLinkURL)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
@objc protocol RoomCoordinatorBridgePresenterDelegate {
|
||||
func roomCoordinatorBridgePresenterDidLeaveRoom(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
func roomCoordinatorBridgePresenterDidCancelRoomPreview(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
func roomCoordinatorBridgePresenter(_ bridgePresenter: RoomCoordinatorBridgePresenter, didSelectRoomWithId roomId: String)
|
||||
func roomCoordinatorBridgePresenterDidDismissInteractively(_ bridgePresenter: RoomCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
class RoomCoordinatorBridgePresenterParameters: NSObject {
|
||||
|
||||
/// The matrix session in which the room should be available.
|
||||
let session: MXSession
|
||||
|
||||
/// The room identifier
|
||||
let roomId: String
|
||||
|
||||
/// If not nil, the room will be opened on this event.
|
||||
let eventId: String?
|
||||
|
||||
/// The data for the room preview.
|
||||
let previewData: RoomPreviewData?
|
||||
|
||||
init(session: MXSession,
|
||||
roomId: String,
|
||||
eventId: String?,
|
||||
previewData: RoomPreviewData?) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.previewData = previewData
|
||||
}
|
||||
}
|
||||
|
||||
/// RoomCoordinatorBridgePresenter enables to start RoomCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class RoomCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let bridgeParameters: RoomCoordinatorBridgePresenterParameters
|
||||
private var coordinator: RoomCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: RoomCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: RoomCoordinatorBridgePresenterParameters) {
|
||||
self.bridgeParameters = parameters
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
|
||||
let coordinator = self.createRoomCoordinator()
|
||||
coordinator.delegate = self
|
||||
let presentable = coordinator.toPresentable()
|
||||
presentable.modalPresentationStyle = .formSheet
|
||||
viewController.present(presentable, animated: animated, completion: nil)
|
||||
coordinator.start()
|
||||
|
||||
self.coordinator = coordinator
|
||||
}
|
||||
|
||||
func push(from navigationController: UINavigationController, animated: Bool) {
|
||||
|
||||
let navigationRouter = NavigationRouterStore.shared.getOrCreateNavigationRouter(for: navigationController)
|
||||
|
||||
let coordinator = self.createRoomCoordinator(with: navigationRouter)
|
||||
coordinator.delegate = self
|
||||
coordinator.start() // Will trigger view controller push
|
||||
|
||||
self.coordinator = coordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func createRoomCoordinator(with navigationRouter: NavigationRouterType = NavigationRouter(navigationController: RiotNavigationController())) -> RoomCoordinator {
|
||||
|
||||
let coordinatorParameters: RoomCoordinatorParameters
|
||||
|
||||
if let previewData = self.bridgeParameters.previewData {
|
||||
coordinatorParameters = RoomCoordinatorParameters(navigationRouter: navigationRouter, previewData: previewData)
|
||||
} else {
|
||||
coordinatorParameters = RoomCoordinatorParameters(navigationRouter: navigationRouter, session: self.bridgeParameters.session, roomId: self.bridgeParameters.roomId, eventId: self.bridgeParameters.eventId)
|
||||
}
|
||||
|
||||
return RoomCoordinator(parameters: coordinatorParameters)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomNotificationSettingsCoordinatorDelegate
|
||||
extension RoomCoordinatorBridgePresenter: RoomCoordinatorDelegate {
|
||||
|
||||
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didSelectRoomWithId roomId: String) {
|
||||
self.delegate?.roomCoordinatorBridgePresenter(self, didSelectRoomWithId: roomId)
|
||||
}
|
||||
|
||||
|
||||
func roomCoordinatorDidLeaveRoom(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidLeaveRoom(self)
|
||||
}
|
||||
|
||||
func roomCoordinatorDidCancelRoomPreview(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidCancelRoomPreview(self)
|
||||
}
|
||||
|
||||
func roomCoordinatorDidDismissInteractively(_ coordinator: RoomCoordinatorProtocol) {
|
||||
self.delegate?.roomCoordinatorBridgePresenterDidDismissInteractively(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// RoomCoordinator input parameters
|
||||
struct RoomCoordinatorParameters {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The navigation router that manage physical navigation
|
||||
let navigationRouter: NavigationRouterType?
|
||||
|
||||
/// The navigation router store that enables to get a NavigationRouter from a navigation controller
|
||||
/// `navigationRouter` property takes priority on `navigationRouterStore`
|
||||
let navigationRouterStore: NavigationRouterStoreProtocol?
|
||||
|
||||
/// The matrix session in which the room should be available.
|
||||
let session: MXSession
|
||||
|
||||
/// The room identifier
|
||||
let roomId: String
|
||||
|
||||
/// If not nil, the room will be opened on this event.
|
||||
let eventId: String?
|
||||
|
||||
/// The data for the room preview.
|
||||
let previewData: RoomPreviewData?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private init(navigationRouter: NavigationRouterType?,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol?,
|
||||
session: MXSession,
|
||||
roomId: String,
|
||||
eventId: String?,
|
||||
previewData: RoomPreviewData?) {
|
||||
self.navigationRouter = navigationRouter
|
||||
self.navigationRouterStore = navigationRouterStore
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
self.eventId = eventId
|
||||
self.previewData = previewData
|
||||
}
|
||||
|
||||
/// Init to present a joined room
|
||||
init(navigationRouter: NavigationRouterType? = nil,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol? = nil,
|
||||
session: MXSession,
|
||||
roomId: String,
|
||||
eventId: String? = nil) {
|
||||
|
||||
self.init(navigationRouter: navigationRouter, navigationRouterStore: navigationRouterStore, session: session, roomId: roomId, eventId: eventId, previewData: nil)
|
||||
}
|
||||
|
||||
/// Init to present a room preview
|
||||
init(navigationRouter: NavigationRouterType? = nil,
|
||||
navigationRouterStore: NavigationRouterStoreProtocol? = nil,
|
||||
previewData: RoomPreviewData) {
|
||||
|
||||
self.init(navigationRouter: navigationRouter, navigationRouterStore: navigationRouterStore, session: previewData.mxSession, roomId: previewData.roomId, eventId: nil, previewData: previewData)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Room Room
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol RoomCoordinatorDelegate: AnyObject {
|
||||
func roomCoordinatorDidLeaveRoom(_ coordinator: RoomCoordinatorProtocol)
|
||||
func roomCoordinatorDidCancelRoomPreview(_ coordinator: RoomCoordinatorProtocol)
|
||||
func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didSelectRoomWithId roomId: String)
|
||||
func roomCoordinatorDidDismissInteractively(_ coordinator: RoomCoordinatorProtocol)
|
||||
}
|
||||
|
||||
/// `RoomCoordinatorProtocol` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol RoomCoordinatorProtocol: Coordinator, Presentable, RoomIdentifiable {
|
||||
var delegate: RoomCoordinatorDelegate? { get }
|
||||
|
||||
var canReleaseRoomDataSource: Bool { get }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `RoomIdentifiable` describes an object tied to a specific room id.
|
||||
/// Useful to identify existing objects that should be removed when the user leaves a room for example.
|
||||
protocol RoomIdentifiable {
|
||||
var roomId: String? { get }
|
||||
var mxSession: MXSession? { get }
|
||||
}
|
||||
Reference in New Issue
Block a user