From 7caa033ca5e7fc432eda066c0d454dc40e208a27 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 10 Aug 2021 20:26:27 +0200 Subject: [PATCH 01/11] [Spaces] M10.8 Browsing users in a space #4682 - Added MVVM classes - roughly integrated `RoomParticipantsViewController` to the MVVM --- Riot/Generated/Storyboards.swift | 5 + .../SideMenu/SideMenuCoordinator.swift | 14 +- .../ShowSpaceMemberListCoordinator.swift | 78 ++++++++ .../ShowSpaceMemberListCoordinatorType.swift | 29 +++ .../ShowSpaceMemberListViewAction.swift | 26 +++ ...owSpaceMemberListViewController.storyboard | 96 ++++++++++ .../ShowSpaceMemberListViewController.swift | 178 ++++++++++++++++++ .../ShowSpaceMemberListViewModel.swift | 91 +++++++++ .../ShowSpaceMemberListViewModelType.swift | 37 ++++ .../ShowSpaceMemberListViewState.swift | 26 +++ .../SpaceMemberListCoordinator.swift | 82 ++++++++ ...MemberListCoordinatorBridgePresenter.swift | 98 ++++++++++ .../SpaceMemberListCoordinatorType.swift | 28 +++ .../Spaces/SpaceMenu/SpaceMenuViewModel.swift | 3 +- 14 files changed, 787 insertions(+), 4 deletions(-) create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 2b7903b2d4..dc2fc972ed 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -244,6 +244,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: ShowSpaceExploreRoomViewController.self) } + internal enum ShowSpaceMemberListViewController: StoryboardType { + internal static let storyboardName = "ShowSpaceMemberListViewController" + + internal static let initialScene = InitialSceneType(storyboard: ShowSpaceMemberListViewController.self) + } internal enum SideMenuViewController: StoryboardType { internal static let storyboardName = "SideMenuViewController" diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index fdbf17d6f4..488e48c9b6 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -62,6 +62,7 @@ final class SideMenuCoordinator: SideMenuCoordinatorType { let spaceMenuPresenter = SpaceMenuPresenter() private var exploreRoomCoordimator: ExploreRoomCoordinatorBridgePresenter? + private var memberListCoordinator: SpaceMemberListCoordinatorBridgePresenter? // MARK: Public @@ -302,8 +303,9 @@ extension SideMenuCoordinator: SpaceMenuPresenterDelegate { self.exploreRoomCoordimator?.delegate = self self.exploreRoomCoordimator?.present(from: self.sideMenuNavigationViewController, animated: true) case .exploreMembers: - // TODO present members list - break + self.memberListCoordinator = SpaceMemberListCoordinatorBridgePresenter(session: session, spaceId: spaceId) + self.memberListCoordinator?.delegate = self + self.memberListCoordinator?.present(from: self.sideMenuNavigationViewController, animated: true) } } } @@ -316,3 +318,11 @@ extension SideMenuCoordinator: ExploreRoomCoordinatorBridgePresenterDelegate { coordinatorBridgePresenter.dismiss(animated: true, completion: nil) } } + +// MARK: - SpaceMemberListCoordinatorBridgePresenterDelegate +extension SideMenuCoordinator: SpaceMemberListCoordinatorBridgePresenterDelegate { + func spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SpaceMemberListCoordinatorBridgePresenter) { + self.memberListCoordinator = nil + coordinatorBridgePresenter.dismiss(animated: true, completion: nil) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift new file mode 100644 index 0000000000..d7fe0b11fe --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift @@ -0,0 +1,78 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 ShowSpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let spaceId: String + private var showSpaceMemberListViewModel: ShowSpaceMemberListViewModelType + private let showSpaceMemberListViewController: ShowSpaceMemberListViewController + private let roomParticipantsViewController: RoomParticipantsViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: ShowSpaceMemberListCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, spaceId: String) { + self.session = session + self.spaceId = spaceId + + let showSpaceMemberListViewModel = ShowSpaceMemberListViewModel(session: self.session) + let showSpaceMemberListViewController = ShowSpaceMemberListViewController.instantiate(with: showSpaceMemberListViewModel) + self.showSpaceMemberListViewModel = showSpaceMemberListViewModel + self.showSpaceMemberListViewController = showSpaceMemberListViewController + // TODO adapt RoomParticipantsViewController to work with the view model + self.roomParticipantsViewController = RoomParticipantsViewController() + self.roomParticipantsViewController.mxRoom = self.session.room(withRoomId: self.spaceId) + } + + // MARK: - Public methods + + func start() { +// self.showSpaceMemberListViewModel.coordinatorDelegate = self +// self.roomParticipantsViewController.mxRoom = self.session.room(withRoomId: self.spaceId) + } + + func toPresentable() -> UIViewController { + return self.roomParticipantsViewController + } +} + +// MARK: - ShowSpaceMemberListViewModelCoordinatorDelegate +extension ShowSpaceMemberListCoordinator: ShowSpaceMemberListViewModelCoordinatorDelegate { + + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) { + self.delegate?.showSpaceMemberListCoordinator(self, didCompleteWithUserDisplayName: userDisplayName) + } + + func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) { + self.delegate?.showSpaceMemberListCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift new file mode 100644 index 0000000000..139988fcc2 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 ShowSpaceMemberListCoordinatorDelegate: AnyObject { + func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) + func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) +} + +/// `ShowSpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol ShowSpaceMemberListCoordinatorType: Coordinator, Presentable { + var delegate: ShowSpaceMemberListCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift new file mode 100644 index 0000000000..24073356e3 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 + +/// ShowSpaceMemberListViewController view actions exposed to view model +enum ShowSpaceMemberListViewAction { + case loadData + case complete + case cancel +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard new file mode 100644 index 0000000000..8757d8e735 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift new file mode 100644 index 0000000000..91d6f456c2 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift @@ -0,0 +1,178 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 UIKit + +final class ShowSpaceMemberListViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet private weak var doneButton: UIButton! + + // MARK: Private + + private var viewModel: ShowSpaceMemberListViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: ShowSpaceMemberListViewModelType) -> ShowSpaceMemberListViewController { + let viewController = StoryboardScene.ShowSpaceMemberListViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .loadData) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: Set view colors here + self.informationLabel.textColor = theme.textPrimaryColor + + self.doneButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.doneButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.title = "Template" + + self.scrollView.keyboardDismissMode = .interactive + + self.informationLabel.text = "VectorL10n.showSpaceMemberListTitle" + } + + private func render(viewState: ShowSpaceMemberListViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded(let displayName): + self.renderLoaded(displayName: displayName) + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + self.informationLabel.text = "Fetch display name" + } + + private func renderLoaded(displayName: String) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.informationLabel.text = "You display name: \(displayName)" + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func doneButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .complete) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - ShowSpaceMemberListViewModelViewDelegate +extension ShowSpaceMemberListViewController: ShowSpaceMemberListViewModelViewDelegate { + + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didUpdateViewState viewSate: ShowSpaceMemberListViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift new file mode 100644 index 0000000000..8e4b990685 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift @@ -0,0 +1,91 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 + +final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + + private var currentOperation: MXHTTPOperation? + private var userDisplayName: String? + + // MARK: Public + + weak var viewDelegate: ShowSpaceMemberListViewModelViewDelegate? + weak var coordinatorDelegate: ShowSpaceMemberListViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + } + + deinit { + self.cancelOperations() + } + + // MARK: - Public + + func process(viewAction: ShowSpaceMemberListViewAction) { + switch viewAction { + case .loadData: + self.loadData() + case .complete: + self.coordinatorDelegate?.showSpaceMemberListViewModel(self, didCompleteWithUserDisplayName: self.userDisplayName) + case .cancel: + self.cancelOperations() + self.coordinatorDelegate?.showSpaceMemberListViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func loadData() { + + self.update(viewState: .loading) + + // Check first that the user homeserver is federated with the Riot-bot homeserver + self.currentOperation = self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in + + guard let self = self else { + return + } + + switch response { + case .success(let userDisplayName): + self.update(viewState: .loaded(userDisplayName)) + self.userDisplayName = userDisplayName + case .failure(let error): + self.update(viewState: .error(error)) + } + } + } + + private func update(viewState: ShowSpaceMemberListViewState) { + self.viewDelegate?.showSpaceMemberListViewModel(self, didUpdateViewState: viewState) + } + + private func cancelOperations() { + self.currentOperation?.cancel() + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift new file mode 100644 index 0000000000..d221433509 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 ShowSpaceMemberListViewModelViewDelegate: AnyObject { + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didUpdateViewState viewSate: ShowSpaceMemberListViewState) +} + +protocol ShowSpaceMemberListViewModelCoordinatorDelegate: AnyObject { + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) + func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) +} + +/// Protocol describing the view model used by `ShowSpaceMemberListViewController` +protocol ShowSpaceMemberListViewModelType { + + var viewDelegate: ShowSpaceMemberListViewModelViewDelegate? { get set } + var coordinatorDelegate: ShowSpaceMemberListViewModelCoordinatorDelegate? { get set } + + func process(viewAction: ShowSpaceMemberListViewAction) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift new file mode 100644 index 0000000000..8460f511c5 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 + +/// ShowSpaceMemberListViewController view state +enum ShowSpaceMemberListViewState { + case loading + case loaded(_ displayName: String) + case error(Error) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift new file mode 100644 index 0000000000..9e75f582b3 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift @@ -0,0 +1,82 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Spaces/SpaceMembers SpaceMemberList ShowSpaceMemberList +/* + 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 UIKit + +@objcMembers +final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let session: MXSession + private let spaceId: String + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SpaceMemberListCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, spaceId: String) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.session = session + self.spaceId = spaceId + } + + // MARK: - Public methods + + func start() { + + let rootCoordinator = self.createShowSpaceMemberListCoordinator() + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + self.navigationRouter.setRootModule(rootCoordinator) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createShowSpaceMemberListCoordinator() -> ShowSpaceMemberListCoordinator { + let coordinator = ShowSpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) + coordinator.delegate = self + return coordinator + } +} + +// MARK: - ShowSpaceMemberListCoordinatorDelegate +extension SpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorDelegate { + func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) { + self.delegate?.spaceMemberListCoordinatorDidComplete(self) + } + + func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) { + self.delegate?.spaceMemberListCoordinatorDidComplete(self) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift new file mode 100644 index 0000000000..bc719aa901 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift @@ -0,0 +1,98 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Spaces/SpaceMembers SpaceMemberList ShowSpaceMemberList +/* + 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 SpaceMemberListCoordinatorBridgePresenterDelegate { + func spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SpaceMemberListCoordinatorBridgePresenter) +} + +/// SpaceMemberListCoordinatorBridgePresenter enables to start SpaceMemberListCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (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 SpaceMemberListCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let spaceId: String + private var coordinator: SpaceMemberListCoordinator? + + // MARK: Public + + weak var delegate: SpaceMemberListCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession, spaceId: String) { + self.session = session + self.spaceId = spaceId + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let spaceMemberListCoordinator = SpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) + spaceMemberListCoordinator.delegate = self + let presentable = spaceMemberListCoordinator.toPresentable() + presentable.presentationController?.delegate = self + viewController.present(presentable, animated: animated, completion: nil) + spaceMemberListCoordinator.start() + + self.coordinator = spaceMemberListCoordinator + } + + 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: - SpaceMemberListCoordinatorDelegate +extension SpaceMemberListCoordinatorBridgePresenter: SpaceMemberListCoordinatorDelegate { + func spaceMemberListCoordinatorDidComplete(_ coordinator: SpaceMemberListCoordinatorType) { + self.delegate?.spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(self) + } +} + +// MARK: - UIAdaptivePresentationControllerDelegate + +extension SpaceMemberListCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { + + func spaceMemberListCoordinatorDidComplete(_ presentationController: UIPresentationController) { + self.delegate?.spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(self) + } + +} diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift new file mode 100644 index 0000000000..6079d616cd --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift @@ -0,0 +1,28 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Spaces/SpaceMembers SpaceMemberList ShowSpaceMemberList +/* + 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 SpaceMemberListCoordinatorDelegate: AnyObject { + func spaceMemberListCoordinatorDidComplete(_ coordinator: SpaceMemberListCoordinatorType) +} + +/// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol SpaceMemberListCoordinatorType: Coordinator, Presentable { + var delegate: SpaceMemberListCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift index 47b72352d4..cace82e61f 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift @@ -33,8 +33,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { weak var viewDelegate: SpaceMenuViewModelViewDelegate? var menuItems: [SpaceMenuListItemViewData] = [ - // TODO: Add list item when implementing actions -// SpaceMenuListItemViewData(actionId: ActionId.members.rawValue, style: .normal, title: VectorL10n.roomDetailsPeople, icon: UIImage(named: "space_menu_members")), + SpaceMenuListItemViewData(actionId: ActionId.members.rawValue, style: .normal, title: VectorL10n.roomDetailsPeople, icon: UIImage(named: "space_menu_members")), SpaceMenuListItemViewData(actionId: ActionId.rooms.rawValue, style: .normal, title: VectorL10n.spacesExploreRooms, icon: UIImage(named: "space_menu_rooms")), SpaceMenuListItemViewData(actionId: ActionId.leave.rawValue, style: .destructive, title: VectorL10n.leave, icon: UIImage(named: "space_menu_leave")) ] From 91d9169da619e815b8bf9c05e260ff5e8565e457 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Sun, 22 Aug 2021 08:48:54 +0300 Subject: [PATCH 02/11] [Spaces] M10.8 Browsing users in a space #4682 - Added navigation to member detail page --- Riot/Assets/en.lproj/Vector.strings | 2 + Riot/Generated/Storyboards.swift | 5 - Riot/Generated/Strings.swift | 8 + .../Detail/RoomMemberDetailsViewController.m | 30 +++- .../Members/RoomParticipantsViewController.m | 37 ++-- .../ShowSpaceMemberDetailCoordinator.swift | 96 +++++++++++ ...ShowSpaceMemberDetailCoordinatorType.swift | 29 ++++ .../ShowSpaceMemberDetailViewAction.swift | 26 +++ .../ShowSpaceMemberDetailViewController.swift | 161 ++++++++++++++++++ .../ShowSpaceMemberDetailViewModel.swift | 112 ++++++++++++ .../ShowSpaceMemberDetailViewModelType.swift | 37 ++++ .../ShowSpaceMemberDetailViewState.swift | 26 +++ .../ShowSpaceMemberListCoordinator.swift | 17 +- .../ShowSpaceMemberListCoordinatorType.swift | 2 +- .../ShowSpaceMemberListViewAction.swift | 2 +- ...owSpaceMemberListViewController.storyboard | 96 ----------- .../ShowSpaceMemberListViewController.swift | 110 +++++++----- .../ShowSpaceMemberListViewModel.swift | 27 +-- .../ShowSpaceMemberListViewModelType.swift | 2 +- .../ShowSpaceMemberListViewState.swift | 2 +- .../SpaceMemberListCoordinator.swift | 72 +++++++- ...MemberListCoordinatorBridgePresenter.swift | 17 +- .../SpaceMemberListCoordinatorType.swift | 3 +- Riot/SupportingFiles/Riot-Bridging-Header.h | 1 + 24 files changed, 710 insertions(+), 210 deletions(-) create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 2fa61ca147..a7422f83a4 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1685,6 +1685,8 @@ Tap the + to start adding people."; "spaces_no_member_found_detail" = "Looking for someone not in space name? For now, you can invite them on web or desktop."; "spaces_coming_soon_title" = "Coming soon"; "spaces_coming_soon_detail" = "This feature hasn’t been implemented here, but it’s on the way. For now, you can do that with Element on your computer."; +"space_participants_action_remove" = "Remove from this space"; +"space_participants_action_ban" = "Ban from this space"; // Mark: Avatar diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index dc2fc972ed..2b7903b2d4 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -244,11 +244,6 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: ShowSpaceExploreRoomViewController.self) } - internal enum ShowSpaceMemberListViewController: StoryboardType { - internal static let storyboardName = "ShowSpaceMemberListViewController" - - internal static let initialScene = InitialSceneType(storyboard: ShowSpaceMemberListViewController.self) - } internal enum SideMenuViewController: StoryboardType { internal static let storyboardName = "SideMenuViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 20a6cd0962..2f321b2f48 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4722,6 +4722,14 @@ internal enum VectorL10n { internal static var spaceFeatureUnavailableTitle: String { return VectorL10n.tr("Vector", "space_feature_unavailable_title") } + /// Ban from this space + internal static var spaceParticipantsActionBan: String { + return VectorL10n.tr("Vector", "space_participants_action_ban") + } + /// Remove from this space + internal static var spaceParticipantsActionRemove: String { + return VectorL10n.tr("Vector", "space_participants_action_remove") + } /// space internal static var spaceTag: String { return VectorL10n.tr("Vector", "space_tag") diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m index ed313cffb3..517a44fa59 100644 --- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m +++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m @@ -465,6 +465,11 @@ - (void)presentCompleteSecurity [[AppDelegate theDelegate] presentCompleteSecurityForSession:self.mainSession]; } +- (void)showRoomWithId:(NSString*)roomId +{ + [[AppDelegate theDelegate] showRoom:roomId andEventId:nil withMatrixSession:self.mainSession]; +} + #pragma mark - Hide/Show navigation bar border - (void)hideNavigationBarBorder:(BOOL)isHidden @@ -518,7 +523,10 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { isOneself = YES; - [otherActionsArray addObject:@(MXKRoomMemberDetailsActionLeave)]; + if (self.enableLeave) + { + [otherActionsArray addObject:@(MXKRoomMemberDetailsActionLeave)]; + } if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels]) { @@ -754,10 +762,24 @@ - (NSString*)actionButtonTitle:(MXKRoomMemberDetailsAction)action title = NSLocalizedStringFromTable(@"room_participants_action_leave", @"Vector", nil); break; case MXKRoomMemberDetailsActionKick: - title = NSLocalizedStringFromTable(@"room_participants_action_remove", @"Vector", nil); + if (self.mxRoom.summary.roomType == MXRoomTypeSpace) + { + title = NSLocalizedStringFromTable(@"space_participants_action_remove", @"Vector", nil); + } + else + { + title = NSLocalizedStringFromTable(@"room_participants_action_remove", @"Vector", nil); + } break; case MXKRoomMemberDetailsActionBan: - title = NSLocalizedStringFromTable(@"room_participants_action_ban", @"Vector", nil); + if (self.mxRoom.summary.roomType == MXRoomTypeSpace) + { + title = NSLocalizedStringFromTable(@"space_participants_action_ban", @"Vector", nil); + } + else + { + title = NSLocalizedStringFromTable(@"room_participants_action_ban", @"Vector", nil); + } break; case MXKRoomMemberDetailsActionUnban: title = NSLocalizedStringFromTable(@"room_participants_action_unban", @"Vector", nil); @@ -1047,7 +1069,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIn if (indexPath.row < directChatsArray.count) { // Open this room - [[AppDelegate theDelegate] showRoom:directChatsArray[indexPath.row] andEventId:nil withMatrixSession:self.mainSession]; + [self showRoomWithId:directChatsArray[indexPath.row]]; } else { diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.m b/Riot/Modules/Room/Members/RoomParticipantsViewController.m index ec814191df..8d370c58da 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.m +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.m @@ -113,7 +113,11 @@ - (void)viewDidLoad self.navigationItem.title = NSLocalizedStringFromTable(@"room_participants_title", @"Vector", nil); - if (self.mxRoom.isDirect) + if (self.mxRoom.summary.roomType == MXRoomTypeSpace) + { + _searchBarView.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil); + } + else if (self.mxRoom.isDirect) { _searchBarView.placeholder = NSLocalizedStringFromTable(@"room_participants_filter_room_members_for_dm", @"Vector", nil); } @@ -340,7 +344,11 @@ - (void)setMxRoom:(MXRoom *)mxRoom { self.searchBarHeader.hidden = NO; - if (self.mxRoom.isDirect) + if (self.mxRoom.summary.roomType == MXRoomTypeSpace) + { + self.searchBarView.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil); + } + else if (self.mxRoom.isDirect) { self.searchBarView.placeholder = NSLocalizedStringFromTable(@"room_participants_filter_room_members_for_dm", @"Vector", nil); } @@ -870,6 +878,19 @@ - (void)pushViewController:(UIViewController*)viewController } } +- (void)showDetailFor:(MXRoomMember* _Nonnull)member from:(UIView* _Nullable)sourceView { + memberDetailsViewController = [RoomMemberDetailsViewController roomMemberDetailsViewController]; + + // Set delegate to handle action on member (start chat, mention) + memberDetailsViewController.delegate = self; + memberDetailsViewController.enableMention = _enableMention; + memberDetailsViewController.enableVoipCall = NO; + + [memberDetailsViewController displayRoomMember:member withMatrixRoom:self.mxRoom]; + + [self pushViewController:memberDetailsViewController]; +} + #pragma mark - UITableView data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView @@ -1185,16 +1206,8 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath if (contact.mxMember) { - memberDetailsViewController = [RoomMemberDetailsViewController roomMemberDetailsViewController]; - - // Set delegate to handle action on member (start chat, mention) - memberDetailsViewController.delegate = self; - memberDetailsViewController.enableMention = _enableMention; - memberDetailsViewController.enableVoipCall = NO; - - [memberDetailsViewController displayRoomMember:contact.mxMember withMatrixRoom:self.mxRoom]; - - [self pushViewController:memberDetailsViewController]; + UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath]; + [self showDetailFor:contact.mxMember from:selectedCell]; } [tableView deselectRowAtIndexPath:indexPath animated:YES]; diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift new file mode 100644 index 0000000000..be29d76b3e --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift @@ -0,0 +1,96 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 ShowSpaceMemberDetailCoordinator: NSObject, ShowSpaceMemberDetailCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let member: MXRoomMember + private let spaceId: String + + private var showSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType + private let showSpaceMemberDetailViewController: ShowSpaceMemberDetailViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: ShowSpaceMemberDetailCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, member: MXRoomMember, spaceId: String) { + self.session = session + self.member = member + self.spaceId = spaceId + + let showSpaceMemberDetailViewModel = ShowSpaceMemberDetailViewModel(session: self.session, member: self.member) + let showSpaceMemberDetailViewController = ShowSpaceMemberDetailViewController.instantiate(with: showSpaceMemberDetailViewModel) + showSpaceMemberDetailViewController.enableMention = true + showSpaceMemberDetailViewController.enableVoipCall = false + showSpaceMemberDetailViewController.enableLeave = false + + self.showSpaceMemberDetailViewModel = showSpaceMemberDetailViewModel + self.showSpaceMemberDetailViewController = showSpaceMemberDetailViewController + } + + // MARK: - Public methods + + func start() { + self.showSpaceMemberDetailViewModel.coordinatorDelegate = self + if let space = self.session.spaceService.getSpace(withId: spaceId) { + // Set delegate to handle action on member (start chat, mention) + self.showSpaceMemberDetailViewController.delegate = self + self.showSpaceMemberDetailViewController.display(self.member, withMatrixRoom: space.room) + } + } + + func toPresentable() -> UIViewController { + return self.showSpaceMemberDetailViewController + } +} + +// MARK: - ShowSpaceMemberDetailViewModelCoordinatorDelegate +extension ShowSpaceMemberDetailCoordinator: ShowSpaceMemberDetailViewModelCoordinatorDelegate { + + func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, showRoomWithId roomId: String) { + self.delegate?.showSpaceMemberDetailCoordinator(self, showRoomWithId: roomId) + } + + func showSpaceMemberDetailViewModelDidCancel(_ viewModel: ShowSpaceMemberDetailViewModelType) { + self.delegate?.showSpaceMemberDetailCoordinatorDidCancel(self) + } + +} + +// MARK: - MXKRoomMemberDetailsViewControllerDelegate +extension ShowSpaceMemberDetailCoordinator: MXKRoomMemberDetailsViewControllerDelegate { + + func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, startChatWithMemberId memberId: String!, completion: (() -> Void)!) { + completion() + self.showSpaceMemberDetailViewModel.process(viewAction: .createRoom(memberId)) + } + +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift new file mode 100644 index 0000000000..2c6de6e975 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 ShowSpaceMemberDetailCoordinatorDelegate: AnyObject { + func showSpaceMemberDetailCoordinator(_ coordinator: ShowSpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) + func showSpaceMemberDetailCoordinatorDidCancel(_ coordinator: ShowSpaceMemberDetailCoordinatorType) +} + +/// `ShowSpaceMemberDetailCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol ShowSpaceMemberDetailCoordinatorType: Coordinator, Presentable { + var delegate: ShowSpaceMemberDetailCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift new file mode 100644 index 0000000000..f13fcbce23 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 + +/// ShowSpaceMemberDetailViewController view actions exposed to view model +enum ShowSpaceMemberDetailViewAction { + case openRoom(_ roomId: String) + case createRoom(_ memberId: String) + case cancel +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift new file mode 100644 index 0000000000..a0596a70ba --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift @@ -0,0 +1,161 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 UIKit + +final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController { + + // MARK: - Constants + + override class func nib() -> UINib! { + return UINib(nibName: "RoomMemberDetailsViewController", bundle: Bundle(for: RoomMemberDetailsViewController.self)) + } + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + // MARK: Private + + private var viewModel: ShowSpaceMemberDetailViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: ShowSpaceMemberDetailViewModelType) -> ShowSpaceMemberDetailViewController { + let viewController = ShowSpaceMemberDetailViewController() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.tableView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + } + + private func render(viewState: ShowSpaceMemberDetailViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + // MARK: - RoomMemberDetailsViewController private + + @objc private func showRoom(withId roomId: String!) { + self.viewModel.process(viewAction: .openRoom(roomId)) + } + + // MARK: - Actions + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - ShowSpaceMemberDetailViewModelViewDelegate +extension ShowSpaceMemberDetailViewController: ShowSpaceMemberDetailViewModelViewDelegate { + + func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, didUpdateViewState viewSate: ShowSpaceMemberDetailViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift new file mode 100644 index 0000000000..5ee184369f --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift @@ -0,0 +1,112 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 + +final class ShowSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let member: MXRoomMember + + private var currentOperation: MXHTTPOperation? + + // MARK: Public + + weak var viewDelegate: ShowSpaceMemberDetailViewModelViewDelegate? + weak var coordinatorDelegate: ShowSpaceMemberDetailViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, member: MXRoomMember) { + self.session = session + self.member = member + } + + deinit { + self.cancelOperations() + } + + // MARK: - Public + + func process(viewAction: ShowSpaceMemberDetailViewAction) { + switch viewAction { + case .openRoom(let roomId): + self.coordinatorDelegate?.showSpaceMemberDetailViewModel(self, showRoomWithId: roomId) + case .createRoom(let memberId): + self.createDirectRoom(forMemberWithId: memberId) + case .cancel: + self.cancelOperations() + self.coordinatorDelegate?.showSpaceMemberDetailViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func update(viewState: ShowSpaceMemberDetailViewState) { + self.viewDelegate?.showSpaceMemberDetailViewModel(self, didUpdateViewState: viewState) + } + + private func createDirectRoom(forMemberWithId memberId: String) { + self.update(viewState: .loading) + AppDelegate.theDelegate().selectMatrixAccount { account in + guard let account = account, let session = account.mxSession else { + self.update(viewState: .loaded) + return + } + + let invite: [String]? = (session.myUserId != memberId) ? [memberId] : nil + self.currentOperation = session.vc_canEnableE2EByDefaultInNewRoom(withUsers: invite) { canEnableE2E in + self.currentOperation = nil + let roomCreationParameters = MXRoomCreationParameters() + roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate + roomCreationParameters.inviteArray = invite + roomCreationParameters.isDirect = !(invite?.isEmpty ?? true) + roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat + + if canEnableE2E { + roomCreationParameters.initialStateEvents = [MXRoomCreationParameters.initialStateEventForEncryption(withAlgorithm: kMXCryptoMegolmAlgorithm)] + } + + self.currentOperation = session.createRoom(parameters: roomCreationParameters) { response in + self.currentOperation = nil + self.update(viewState: .loaded) + guard response.isSuccess, let room = response.value else { + if let error = response.error { + self.update(viewState: .error(error)) + } + return + } + self.coordinatorDelegate?.showSpaceMemberDetailViewModel(self, showRoomWithId: room.roomId) + } + } failure: { error in + self.update(viewState: .loaded) + if let error = error { + self.update(viewState: .error(error)) + } + } + } + } + + private func cancelOperations() { + self.currentOperation?.cancel() + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift new file mode 100644 index 0000000000..48d3eeef0a --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 ShowSpaceMemberDetailViewModelViewDelegate: AnyObject { + func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, didUpdateViewState viewSate: ShowSpaceMemberDetailViewState) +} + +protocol ShowSpaceMemberDetailViewModelCoordinatorDelegate: AnyObject { + func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, showRoomWithId roomId: String) + func showSpaceMemberDetailViewModelDidCancel(_ viewModel: ShowSpaceMemberDetailViewModelType) +} + +/// Protocol describing the view model used by `ShowSpaceMemberDetailViewController` +protocol ShowSpaceMemberDetailViewModelType { + + var viewDelegate: ShowSpaceMemberDetailViewModelViewDelegate? { get set } + var coordinatorDelegate: ShowSpaceMemberDetailViewModelCoordinatorDelegate? { get set } + + func process(viewAction: ShowSpaceMemberDetailViewAction) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift new file mode 100644 index 0000000000..5d81fd2746 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 + +/// ShowSpaceMemberDetailViewController view state +enum ShowSpaceMemberDetailViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift index d7fe0b11fe..6ea9ecaf94 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift @@ -29,7 +29,6 @@ final class ShowSpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorType { private let spaceId: String private var showSpaceMemberListViewModel: ShowSpaceMemberListViewModelType private let showSpaceMemberListViewController: ShowSpaceMemberListViewController - private let roomParticipantsViewController: RoomParticipantsViewController // MARK: Public @@ -44,32 +43,28 @@ final class ShowSpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorType { self.session = session self.spaceId = spaceId - let showSpaceMemberListViewModel = ShowSpaceMemberListViewModel(session: self.session) + let showSpaceMemberListViewModel = ShowSpaceMemberListViewModel(session: self.session, spaceId: self.spaceId) let showSpaceMemberListViewController = ShowSpaceMemberListViewController.instantiate(with: showSpaceMemberListViewModel) self.showSpaceMemberListViewModel = showSpaceMemberListViewModel self.showSpaceMemberListViewController = showSpaceMemberListViewController - // TODO adapt RoomParticipantsViewController to work with the view model - self.roomParticipantsViewController = RoomParticipantsViewController() - self.roomParticipantsViewController.mxRoom = self.session.room(withRoomId: self.spaceId) } // MARK: - Public methods func start() { -// self.showSpaceMemberListViewModel.coordinatorDelegate = self -// self.roomParticipantsViewController.mxRoom = self.session.room(withRoomId: self.spaceId) + self.showSpaceMemberListViewModel.coordinatorDelegate = self } func toPresentable() -> UIViewController { - return self.roomParticipantsViewController + return self.showSpaceMemberListViewController } } // MARK: - ShowSpaceMemberListViewModelCoordinatorDelegate extension ShowSpaceMemberListCoordinator: ShowSpaceMemberListViewModelCoordinatorDelegate { - - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) { - self.delegate?.showSpaceMemberListCoordinator(self, didCompleteWithUserDisplayName: userDisplayName) + + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.delegate?.showSpaceMemberListCoordinator(self, didSelect: member, from: sourceView) } func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) { diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift index 139988fcc2..831289929d 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift @@ -19,7 +19,7 @@ import Foundation protocol ShowSpaceMemberListCoordinatorDelegate: AnyObject { - func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) + func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift index 24073356e3..33c6025348 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift @@ -21,6 +21,6 @@ import Foundation /// ShowSpaceMemberListViewController view actions exposed to view model enum ShowSpaceMemberListViewAction { case loadData - case complete + case complete(_ selectedMember: MXRoomMember, _ sourceView: UIView?) case cancel } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard deleted file mode 100644 index 8757d8e735..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.storyboard +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift index 91d6f456c2..6eff35eef2 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift @@ -18,37 +18,32 @@ import UIKit -final class ShowSpaceMemberListViewController: UIViewController { +final class ShowSpaceMemberListViewController: RoomParticipantsViewController { // MARK: - Constants - private enum Constants { - static let aConstant: Int = 666 - } - // MARK: - Properties - // MARK: Outlets - - @IBOutlet private weak var scrollView: UIScrollView! - - @IBOutlet private weak var informationLabel: UILabel! - @IBOutlet private weak var doneButton: UIButton! - // MARK: Private private var viewModel: ShowSpaceMemberListViewModelType! private var theme: Theme! - private var keyboardAvoider: KeyboardAvoider? private var errorPresenter: MXKErrorPresentation! private var activityPresenter: ActivityIndicatorPresenter! + private var titleView: MainTitleView! + private var emptyView: RootTabEmptyView! + private var emptyViewArtwork: UIImage { + return ThemeService.shared().isCurrentThemeDark() ? Asset.Images.peopleEmptyScreenArtworkDark.image : Asset.Images.peopleEmptyScreenArtwork.image + } + // MARK: - Setup class func instantiate(with viewModel: ShowSpaceMemberListViewModelType) -> ShowSpaceMemberListViewController { - let viewController = StoryboardScene.ShowSpaceMemberListViewController.initialScene.instantiate() + let viewController = ShowSpaceMemberListViewController() viewController.viewModel = viewModel viewController.theme = ThemeService.shared().theme + viewController.emptyView = RootTabEmptyView.instantiate() return viewController } @@ -60,7 +55,6 @@ final class ShowSpaceMemberListViewController: UIViewController { // Do any additional setup after loading the view. self.setupViews() - self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) self.activityPresenter = ActivityIndicatorPresenter() self.errorPresenter = MXKErrorAlertPresentation() @@ -70,20 +64,10 @@ final class ShowSpaceMemberListViewController: UIViewController { self.viewModel.viewDelegate = self self.viewModel.process(viewAction: .loadData) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - self.keyboardAvoider?.startAvoiding() + self.title = "" } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - - self.keyboardAvoider?.stopAvoiding() - } - + override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle } @@ -98,13 +82,9 @@ final class ShowSpaceMemberListViewController: UIViewController { if let navigationBar = self.navigationController?.navigationBar { theme.applyStyle(onNavigationBar: navigationBar) } - - - // TODO: Set view colors here - self.informationLabel.textColor = theme.textPrimaryColor - - self.doneButton.backgroundColor = theme.backgroundColor - theme.applyStyle(onButton: self.doneButton) + + theme.applyStyle(onSearchBar: self.searchBarView) + self.titleView.update(theme: theme) } private func registerThemeServiceDidChangeThemeNotification() { @@ -122,19 +102,23 @@ final class ShowSpaceMemberListViewController: UIViewController { self.navigationItem.rightBarButtonItem = cancelBarButtonItem - self.title = "Template" - - self.scrollView.keyboardDismissMode = .interactive + self.titleView = MainTitleView() + self.titleView.titleLabel.text = VectorL10n.roomDetailsPeople + self.navigationItem.titleView = self.titleView - self.informationLabel.text = "VectorL10n.showSpaceMemberListTitle" + self.emptyView.frame = CGRect(x: 0, y: self.searchBarView.frame.maxY, width: self.view.bounds.width, height: self.view.bounds.height - self.searchBarView.frame.maxY) + self.emptyView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + self.emptyView.alpha = 0 + self.view.insertSubview(self.emptyView, at: 0) + self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.spacesNoResultFoundTitle, informationText: VectorL10n.spacesNoMemberFoundDetail) } private func render(viewState: ShowSpaceMemberListViewState) { switch viewState { case .loading: self.renderLoading() - case .loaded(let displayName): - self.renderLoaded(displayName: displayName) + case .loaded(let space): + self.renderLoaded(space: space) case .error(let error): self.render(error: error) } @@ -142,13 +126,12 @@ final class ShowSpaceMemberListViewController: UIViewController { private func renderLoading() { self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) - self.informationLabel.text = "Fetch display name" } - private func renderLoaded(displayName: String) { + private func renderLoaded(space: MXSpace) { self.activityPresenter.removeCurrentActivityIndicator(animated: true) - - self.informationLabel.text = "You display name: \(displayName)" + self.mxRoom = space.room + self.titleView.subtitleLabel.text = space.summary?.displayname } private func render(error: Error) { @@ -156,16 +139,49 @@ final class ShowSpaceMemberListViewController: UIViewController { self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) } + @objc private func showDetail(for member: MXRoomMember, from sourceView: UIView?) { + self.viewModel.process(viewAction: .complete(member, sourceView)) + } // MARK: - Actions - @IBAction private func doneButtonAction(_ sender: Any) { - self.viewModel.process(viewAction: .complete) + @objc private func onAddParticipantButtonPressed() { + self.errorPresenter.presentError(from: self, title: VectorL10n.spacesComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil) } - + private func cancelButtonAction() { self.viewModel.process(viewAction: .cancel) } + + // MARK: - UISearchBarDelegate + + override func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { + return true + } + + override func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool { + return true + } + + override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + super.searchBar(searchBar, textDidChange: searchText) + + UIView.animate(withDuration: 0.2) { + self.emptyView.alpha = self.tableView.numberOfSections == 0 ? 1 : 0 + self.tableView.alpha = self.tableView.numberOfSections == 0 ? 0 : 1 + } + } + + // MARK: - MXKRoomMemberDetailsViewControllerDelegate + + override func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, startChatWithMemberId matrixId: String!, completion: (() -> Void)!) { + completion() + self.errorPresenter.presentError(from: self, title: VectorL10n.spacesComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil) + } + + override func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, placeVoipCallWithMemberId matrixId: String!, andVideo isVideoCall: Bool) { + self.errorPresenter.presentError(from: self, title: VectorL10n.spacesComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil) + } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift index 8e4b990685..ffc078e3ff 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift @@ -25,6 +25,7 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { // MARK: Private private let session: MXSession + private let spaceId: String private var currentOperation: MXHTTPOperation? private var userDisplayName: String? @@ -36,8 +37,9 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { // MARK: - Setup - init(session: MXSession) { + init(session: MXSession, spaceId: String) { self.session = session + self.spaceId = spaceId } deinit { @@ -50,8 +52,8 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { switch viewAction { case .loadData: self.loadData() - case .complete: - self.coordinatorDelegate?.showSpaceMemberListViewModel(self, didCompleteWithUserDisplayName: self.userDisplayName) + case .complete(let selectedMember, let sourceView): + self.coordinatorDelegate?.showSpaceMemberListViewModel(self, didSelect: selectedMember, from: sourceView) case .cancel: self.cancelOperations() self.coordinatorDelegate?.showSpaceMemberListViewModelDidCancel(self) @@ -61,23 +63,8 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { // MARK: - Private private func loadData() { - - self.update(viewState: .loading) - - // Check first that the user homeserver is federated with the Riot-bot homeserver - self.currentOperation = self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in - - guard let self = self else { - return - } - - switch response { - case .success(let userDisplayName): - self.update(viewState: .loaded(userDisplayName)) - self.userDisplayName = userDisplayName - case .failure(let error): - self.update(viewState: .error(error)) - } + if let space = self.session.spaceService.getSpace(withId: spaceId) { + self.update(viewState: .loaded(space)) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift index d221433509..7ab08f313a 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift @@ -23,7 +23,7 @@ protocol ShowSpaceMemberListViewModelViewDelegate: AnyObject { } protocol ShowSpaceMemberListViewModelCoordinatorDelegate: AnyObject { - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) + func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift index 8460f511c5..fd8ad8dda0 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift @@ -21,6 +21,6 @@ import Foundation /// ShowSpaceMemberListViewController view state enum ShowSpaceMemberListViewState { case loading - case loaded(_ displayName: String) + case loaded(_ space: MXSpace) case error(Error) } diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift index 9e75f582b3..4731df629c 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift @@ -28,7 +28,8 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { private let navigationRouter: NavigationRouterType private let session: MXSession private let spaceId: String - + private weak var memberDetailCoordinator: ShowSpaceMemberDetailCoordinator? + // MARK: Public // Must be used only internally @@ -61,6 +62,30 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { return self.navigationRouter.toPresentable() } + func presentMemberDetail(with member: MXRoomMember, from sourceView: UIView?) { + let coordinator = self.createShowSpaceMemberDetailCoordinator(with: member) + coordinator.start() + self.add(childCoordinator: coordinator) + self.memberDetailCoordinator = coordinator + + if UIDevice.current.isPhone { + self.navigationRouter.push(coordinator.toPresentable(), animated: true) { + if let memberDetailCoordinator = self.memberDetailCoordinator { + self.remove(childCoordinator: memberDetailCoordinator) + } + } + } else { + let viewController = coordinator.toPresentable() + viewController.modalPresentationStyle = .popover + if let sourceView = sourceView, let popoverPresentationController = viewController.popoverPresentationController { + popoverPresentationController.sourceView = sourceView + popoverPresentationController.sourceRect = sourceView.bounds + } + + self.navigationRouter.present(viewController, animated: true) + } + } + // MARK: - Private methods private func createShowSpaceMemberListCoordinator() -> ShowSpaceMemberListCoordinator { @@ -68,15 +93,54 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { coordinator.delegate = self return coordinator } + + private func createShowSpaceMemberDetailCoordinator(with member: MXRoomMember) -> ShowSpaceMemberDetailCoordinator { + let coordinator = ShowSpaceMemberDetailCoordinator(session: self.session, member: member, spaceId: self.spaceId) + coordinator.delegate = self + return coordinator + } + + private func navigateTo(roomWith roomId: String) { + let roomDataSourceManager = MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.session) + roomDataSourceManager?.roomDataSource(forRoom: roomId, create: true, onComplete: { [weak self] roomDataSource in + + let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) + guard let roomViewController = storyboard.instantiateViewController(withIdentifier: "RoomViewControllerStoryboardId") as? RoomViewController else { + return + } + + self?.navigationRouter.push(roomViewController, animated: true, popCompletion: nil) + roomViewController.displayRoom(roomDataSource) + roomViewController.navigationItem.leftItemsSupplementBackButton = true + roomViewController.showMissedDiscussionsBadge = false + }) + } } // MARK: - ShowSpaceMemberListCoordinatorDelegate extension SpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorDelegate { - func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) { - self.delegate?.spaceMemberListCoordinatorDidComplete(self) + func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.delegate?.spaceMemberListCoordinator(self, didSelect: member, from: sourceView) } func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) { - self.delegate?.spaceMemberListCoordinatorDidComplete(self) + self.delegate?.spaceMemberListCoordinatorDidCancel(self) + } +} + +extension SpaceMemberListCoordinator: ShowSpaceMemberDetailCoordinatorDelegate { + func showSpaceMemberDetailCoordinator(_ coordinator: ShowSpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) { + if !UIDevice.current.isPhone, let memberDetailCoordinator = self.memberDetailCoordinator { + memberDetailCoordinator.toPresentable().dismiss(animated: true, completion: { + self.memberDetailCoordinator = nil + self.navigateTo(roomWith: roomId) + }) + } else { + self.navigateTo(roomWith: roomId) + } + } + + func showSpaceMemberDetailCoordinatorDidCancel(_ coordinator: ShowSpaceMemberDetailCoordinatorType) { + self.delegate?.spaceMemberListCoordinatorDidCancel(self) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift index bc719aa901..f65b04e267 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift @@ -50,11 +50,6 @@ final class SpaceMemberListCoordinatorBridgePresenter: NSObject { // MARK: - Public - // NOTE: Default value feature is not compatible with Objective-C. - // func present(from viewController: UIViewController, animated: Bool) { - // self.present(from: viewController, animated: animated) - // } - func present(from viewController: UIViewController, animated: Bool) { let spaceMemberListCoordinator = SpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) spaceMemberListCoordinator.delegate = self @@ -78,13 +73,23 @@ final class SpaceMemberListCoordinatorBridgePresenter: NSObject { } } } + + // MARK: - Private + + func navigate(to member: MXRoomMember, from sourceView: UIView?) { + self.coordinator?.presentMemberDetail(with: member, from: sourceView) + } } // MARK: - SpaceMemberListCoordinatorDelegate extension SpaceMemberListCoordinatorBridgePresenter: SpaceMemberListCoordinatorDelegate { - func spaceMemberListCoordinatorDidComplete(_ coordinator: SpaceMemberListCoordinatorType) { + func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) { self.delegate?.spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(self) } + + func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.navigate(to: member, from: sourceView) + } } // MARK: - UIAdaptivePresentationControllerDelegate diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift index 6079d616cd..228ad50827 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift @@ -19,7 +19,8 @@ import Foundation protocol SpaceMemberListCoordinatorDelegate: AnyObject { - func spaceMemberListCoordinatorDidComplete(_ coordinator: SpaceMemberListCoordinatorType) + func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) + func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) } /// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index b4d4b8e00e..613cc61c1d 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -38,3 +38,4 @@ #import "SettingsViewController.h" #import "BugReportViewController.h" #import "BuildInfo.h" +#import "RoomMemberDetailsViewController.h" From 2e690e5f6dbf61d39ede7947eb5476e93fc2be34 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Sun, 22 Aug 2021 09:23:58 +0300 Subject: [PATCH 03/11] [Spaces] M10.8 Browsing users in a space #4682 - Present space members screen from people tab if space has been selected --- Riot/Modules/People/PeopleViewController.m | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index 26b0ca3f09..6000e32fda 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -26,12 +26,14 @@ #import "Riot-Swift.h" -@interface PeopleViewController () +@interface PeopleViewController () { NSInteger directRoomsSectionNumber; RecentsDataSource *recentsDataSource; } +@property(nonatomic) SpaceMemberListCoordinatorBridgePresenter *spaceMemberListCoordinatorBridgePresenter; + @end @implementation PeopleViewController @@ -119,7 +121,16 @@ - (void)refreshCurrentSelectedCell:(BOOL)forceVisible - (void)onPlusButtonPressed { - [self performSegueWithIdentifier:@"presentStartChat" sender:self]; + if (self.dataSource.currentSpace != nil) + { + self.spaceMemberListCoordinatorBridgePresenter = [[SpaceMemberListCoordinatorBridgePresenter alloc] initWithSession:self.mainSession spaceId:self.dataSource.currentSpace.spaceId]; + self.spaceMemberListCoordinatorBridgePresenter.delegate = self; + [self.spaceMemberListCoordinatorBridgePresenter presentFrom:self animated:YES]; + } + else + { + [self performSegueWithIdentifier:@"presentStartChat" sender:self]; + } } #pragma mark - @@ -172,4 +183,13 @@ - (NSUInteger)totalItemCounts + recentsDataSource.conversationCellDataArray.count; } +#pragma mark - SpaceMemberListCoordinatorBridgePresenterDelegate + +- (void)spaceMemberListCoordinatorBridgePresenterDelegateDidComplete:(SpaceMemberListCoordinatorBridgePresenter *)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:^{ + self.spaceMemberListCoordinatorBridgePresenter = nil; + }]; +} + @end From 4e3e803943e7eaa61cfde92917c6214b2c99cd2e Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 2 Sep 2021 09:34:25 +0300 Subject: [PATCH 04/11] [Spaces] M10.8 Browsing users in a space #4682 - Renamed coordinators --- Riot/Modules/People/PeopleViewController.m | 16 ++-- .../SideMenu/SideMenuCoordinator.swift | 47 ++++++--- .../ShowSpaceMemberDetailCoordinator.swift | 96 ------------------- .../ShowSpaceMemberDetailViewModelType.swift | 37 ------- .../SpaceMemberDetailCoordinator.swift | 96 +++++++++++++++++++ ...=> SpaceMemberDetailCoordinatorType.swift} | 12 +-- ...wift => SpaceMemberDetailViewAction.swift} | 4 +- ... => SpaceMemberDetailViewController.swift} | 16 ++-- ...swift => SpaceMemberDetailViewModel.swift} | 18 ++-- .../SpaceMemberDetailViewModelType.swift | 37 +++++++ ...swift => SpaceMemberDetailViewState.swift} | 4 +- .../ShowSpaceMemberListCoordinator.swift | 73 -------------- .../ShowSpaceMemberListCoordinatorType.swift | 29 ------ .../ShowSpaceMemberListViewModelType.swift | 37 ------- .../SpaceMemberListCoordinator.swift | 73 ++++++++++++++ .../SpaceMemberListCoordinatorType.swift | 8 +- ....swift => SpaceMemberListViewAction.swift} | 4 +- ...ft => SpaceMemberListViewController.swift} | 16 ++-- ...l.swift => SpaceMemberListViewModel.swift} | 16 ++-- .../SpaceMemberListViewModelType.swift | 37 +++++++ ...e.swift => SpaceMemberListViewState.swift} | 4 +- ...or.swift => SpaceMembersCoordinator.swift} | 38 ++++---- ...ceMembersCoordinatorBridgePresenter.swift} | 30 +++--- .../SpaceMembersCoordinatorType.swift | 29 ++++++ ...xploreRoomCoordinatorBridgePresenter.swift | 2 +- 25 files changed, 398 insertions(+), 381 deletions(-) delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinator.swift rename Riot/Modules/Spaces/SpaceMembers/MemberDetail/{ShowSpaceMemberDetailCoordinatorType.swift => SpaceMemberDetailCoordinatorType.swift} (55%) rename Riot/Modules/Spaces/SpaceMembers/MemberDetail/{ShowSpaceMemberDetailViewAction.swift => SpaceMemberDetailViewAction.swift} (87%) rename Riot/Modules/Spaces/SpaceMembers/MemberDetail/{ShowSpaceMemberDetailViewController.swift => SpaceMemberDetailViewController.swift} (86%) rename Riot/Modules/Spaces/SpaceMembers/MemberDetail/{ShowSpaceMemberDetailViewModel.swift => SpaceMemberDetailViewModel.swift} (82%) create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModelType.swift rename Riot/Modules/Spaces/SpaceMembers/MemberDetail/{ShowSpaceMemberDetailViewState.swift => SpaceMemberDetailViewState.swift} (89%) delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift delete mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinator.swift rename Riot/Modules/Spaces/SpaceMembers/{ => MemberList}/SpaceMemberListCoordinatorType.swift (85%) rename Riot/Modules/Spaces/SpaceMembers/MemberList/{ShowSpaceMemberListViewAction.swift => SpaceMemberListViewAction.swift} (88%) rename Riot/Modules/Spaces/SpaceMembers/MemberList/{ShowSpaceMemberListViewController.swift => SpaceMemberListViewController.swift} (90%) rename Riot/Modules/Spaces/SpaceMembers/MemberList/{ShowSpaceMemberListViewModel.swift => SpaceMemberListViewModel.swift} (72%) create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModelType.swift rename Riot/Modules/Spaces/SpaceMembers/MemberList/{ShowSpaceMemberListViewState.swift => SpaceMemberListViewState.swift} (90%) rename Riot/Modules/Spaces/SpaceMembers/{SpaceMemberListCoordinator.swift => SpaceMembersCoordinator.swift} (70%) rename Riot/Modules/Spaces/SpaceMembers/{SpaceMemberListCoordinatorBridgePresenter.swift => SpaceMembersCoordinatorBridgePresenter.swift} (64%) create mode 100644 Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index 6000e32fda..53bb377bdd 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -26,13 +26,13 @@ #import "Riot-Swift.h" -@interface PeopleViewController () +@interface PeopleViewController () { NSInteger directRoomsSectionNumber; RecentsDataSource *recentsDataSource; } -@property(nonatomic) SpaceMemberListCoordinatorBridgePresenter *spaceMemberListCoordinatorBridgePresenter; +@property(nonatomic) SpaceMembersCoordinatorBridgePresenter *spaceMembersCoordinatorBridgePresenter; @end @@ -123,9 +123,9 @@ - (void)onPlusButtonPressed { if (self.dataSource.currentSpace != nil) { - self.spaceMemberListCoordinatorBridgePresenter = [[SpaceMemberListCoordinatorBridgePresenter alloc] initWithSession:self.mainSession spaceId:self.dataSource.currentSpace.spaceId]; - self.spaceMemberListCoordinatorBridgePresenter.delegate = self; - [self.spaceMemberListCoordinatorBridgePresenter presentFrom:self animated:YES]; + self.spaceMembersCoordinatorBridgePresenter = [[SpaceMembersCoordinatorBridgePresenter alloc] initWithSession:self.mainSession spaceId:self.dataSource.currentSpace.spaceId]; + self.spaceMembersCoordinatorBridgePresenter.delegate = self; + [self.spaceMembersCoordinatorBridgePresenter presentFrom:self animated:YES]; } else { @@ -183,12 +183,12 @@ - (NSUInteger)totalItemCounts + recentsDataSource.conversationCellDataArray.count; } -#pragma mark - SpaceMemberListCoordinatorBridgePresenterDelegate +#pragma mark - SpaceMembersCoordinatorBridgePresenterDelegate -- (void)spaceMemberListCoordinatorBridgePresenterDelegateDidComplete:(SpaceMemberListCoordinatorBridgePresenter *)coordinatorBridgePresenter +- (void)spaceMembersCoordinatorBridgePresenterDelegateDidComplete:(SpaceMembersCoordinatorBridgePresenter *)coordinatorBridgePresenter { [coordinatorBridgePresenter dismissWithAnimated:YES completion:^{ - self.spaceMemberListCoordinatorBridgePresenter = nil; + self.spaceMembersCoordinatorBridgePresenter = nil; }]; } diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index b2ac5fc9b2..36857a3ae3 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -62,7 +62,7 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { let spaceMenuPresenter = SpaceMenuPresenter() private var exploreRoomCoordinator: ExploreRoomCoordinator? - private var memberListCoordinator: SpaceMemberListCoordinatorBridgePresenter? + private var membersCoordinator: SpaceMembersCoordinator? // MARK: Public @@ -216,6 +216,17 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { self.exploreRoomCoordinator = exploreRoomCoordinator } + + private func showMembers(spaceId: String, session: MXSession) { + let spaceMembersCoordinator = SpaceMembersCoordinator(session: session, spaceId: spaceId) + spaceMembersCoordinator.delegate = self + let presentable = spaceMembersCoordinator.toPresentable() + presentable.presentationController?.delegate = self + self.sideMenuViewController.present(presentable, animated: true, completion: nil) + spaceMembersCoordinator.start() + + self.membersCoordinator = spaceMembersCoordinator + } private func showInviteFriends(from sourceView: UIView?) { let myUserId = self.parameters.userSessionsService.mainUserSession?.userId ?? "" @@ -240,6 +251,10 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { } } + func navigate(to member: MXRoomMember, from sourceView: UIView?) { + self.membersCoordinator?.presentMemberDetail(with: member, from: sourceView) + } + // MARK: UserSessions management private func registerUserSessionsServiceNotifications() { @@ -320,9 +335,7 @@ extension SideMenuCoordinator: SpaceMenuPresenterDelegate { case .exploreRooms: self.showExploreRooms(spaceId: spaceId, session: session) case .exploreMembers: - self.memberListCoordinator = SpaceMemberListCoordinatorBridgePresenter(session: session, spaceId: spaceId) - self.memberListCoordinator?.delegate = self - self.memberListCoordinator?.present(from: self.sideMenuNavigationViewController, animated: true) + self.showMembers(spaceId: spaceId, session: session) } } } @@ -341,20 +354,24 @@ extension SideMenuCoordinator: ExploreRoomCoordinatorDelegate { } } -// MARK: - UIAdaptivePresentationControllerDelegate -extension SideMenuCoordinator: UIAdaptivePresentationControllerDelegate { - - func exploreRoomCoordinatorDidComplete(_ presentationController: UIPresentationController) { - self.exploreRoomCoordinator?.toPresentable().dismiss(animated: true) { - self.exploreRoomCoordinator = nil +// MARK: - SpaceMembersCoordinatorDelegate +extension SideMenuCoordinator: SpaceMembersCoordinatorDelegate { + func spaceMembersCoordinatorDidCancel(_ coordinator: SpaceMembersCoordinatorType) { + self.membersCoordinator?.toPresentable().dismiss(animated: true) { + self.membersCoordinator = nil } } + + func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.navigate(to: member, from: sourceView) + } } -// MARK: - SpaceMemberListCoordinatorBridgePresenterDelegate -extension SideMenuCoordinator: SpaceMemberListCoordinatorBridgePresenterDelegate { - func spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SpaceMemberListCoordinatorBridgePresenter) { - self.memberListCoordinator = nil - coordinatorBridgePresenter.dismiss(animated: true, completion: nil) +// MARK: - UIAdaptivePresentationControllerDelegate +extension SideMenuCoordinator: UIAdaptivePresentationControllerDelegate { + + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.exploreRoomCoordinator = nil + self.membersCoordinator = nil } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift deleted file mode 100644 index be29d76b3e..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinator.swift +++ /dev/null @@ -1,96 +0,0 @@ -// File created from ScreenTemplate -// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail -/* - 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 ShowSpaceMemberDetailCoordinator: NSObject, ShowSpaceMemberDetailCoordinatorType { - - // MARK: - Properties - - // MARK: Private - - private let session: MXSession - private let member: MXRoomMember - private let spaceId: String - - private var showSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType - private let showSpaceMemberDetailViewController: ShowSpaceMemberDetailViewController - - // MARK: Public - - // Must be used only internally - var childCoordinators: [Coordinator] = [] - - weak var delegate: ShowSpaceMemberDetailCoordinatorDelegate? - - // MARK: - Setup - - init(session: MXSession, member: MXRoomMember, spaceId: String) { - self.session = session - self.member = member - self.spaceId = spaceId - - let showSpaceMemberDetailViewModel = ShowSpaceMemberDetailViewModel(session: self.session, member: self.member) - let showSpaceMemberDetailViewController = ShowSpaceMemberDetailViewController.instantiate(with: showSpaceMemberDetailViewModel) - showSpaceMemberDetailViewController.enableMention = true - showSpaceMemberDetailViewController.enableVoipCall = false - showSpaceMemberDetailViewController.enableLeave = false - - self.showSpaceMemberDetailViewModel = showSpaceMemberDetailViewModel - self.showSpaceMemberDetailViewController = showSpaceMemberDetailViewController - } - - // MARK: - Public methods - - func start() { - self.showSpaceMemberDetailViewModel.coordinatorDelegate = self - if let space = self.session.spaceService.getSpace(withId: spaceId) { - // Set delegate to handle action on member (start chat, mention) - self.showSpaceMemberDetailViewController.delegate = self - self.showSpaceMemberDetailViewController.display(self.member, withMatrixRoom: space.room) - } - } - - func toPresentable() -> UIViewController { - return self.showSpaceMemberDetailViewController - } -} - -// MARK: - ShowSpaceMemberDetailViewModelCoordinatorDelegate -extension ShowSpaceMemberDetailCoordinator: ShowSpaceMemberDetailViewModelCoordinatorDelegate { - - func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, showRoomWithId roomId: String) { - self.delegate?.showSpaceMemberDetailCoordinator(self, showRoomWithId: roomId) - } - - func showSpaceMemberDetailViewModelDidCancel(_ viewModel: ShowSpaceMemberDetailViewModelType) { - self.delegate?.showSpaceMemberDetailCoordinatorDidCancel(self) - } - -} - -// MARK: - MXKRoomMemberDetailsViewControllerDelegate -extension ShowSpaceMemberDetailCoordinator: MXKRoomMemberDetailsViewControllerDelegate { - - func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, startChatWithMemberId memberId: String!, completion: (() -> Void)!) { - completion() - self.showSpaceMemberDetailViewModel.process(viewAction: .createRoom(memberId)) - } - -} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift deleted file mode 100644 index 48d3eeef0a..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModelType.swift +++ /dev/null @@ -1,37 +0,0 @@ -// File created from ScreenTemplate -// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail -/* - 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 ShowSpaceMemberDetailViewModelViewDelegate: AnyObject { - func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, didUpdateViewState viewSate: ShowSpaceMemberDetailViewState) -} - -protocol ShowSpaceMemberDetailViewModelCoordinatorDelegate: AnyObject { - func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, showRoomWithId roomId: String) - func showSpaceMemberDetailViewModelDidCancel(_ viewModel: ShowSpaceMemberDetailViewModelType) -} - -/// Protocol describing the view model used by `ShowSpaceMemberDetailViewController` -protocol ShowSpaceMemberDetailViewModelType { - - var viewDelegate: ShowSpaceMemberDetailViewModelViewDelegate? { get set } - var coordinatorDelegate: ShowSpaceMemberDetailViewModelCoordinatorDelegate? { get set } - - func process(viewAction: ShowSpaceMemberDetailViewAction) -} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinator.swift new file mode 100644 index 0000000000..972b5153b4 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinator.swift @@ -0,0 +1,96 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 SpaceMemberDetailCoordinator: NSObject, SpaceMemberDetailCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let member: MXRoomMember + private let spaceId: String + + private var spaceMemberDetailViewModel: SpaceMemberDetailViewModelType + private let spaceMemberDetailViewController: SpaceMemberDetailViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SpaceMemberDetailCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, member: MXRoomMember, spaceId: String) { + self.session = session + self.member = member + self.spaceId = spaceId + + let spaceMemberDetailViewModel = SpaceMemberDetailViewModel(session: self.session, member: self.member) + let spaceMemberDetailViewController = SpaceMemberDetailViewController.instantiate(with: spaceMemberDetailViewModel) + spaceMemberDetailViewController.enableMention = true + spaceMemberDetailViewController.enableVoipCall = false + spaceMemberDetailViewController.enableLeave = false + + self.spaceMemberDetailViewModel = spaceMemberDetailViewModel + self.spaceMemberDetailViewController = spaceMemberDetailViewController + } + + // MARK: - Public methods + + func start() { + self.spaceMemberDetailViewModel.coordinatorDelegate = self + if let space = self.session.spaceService.getSpace(withId: spaceId) { + // Set delegate to handle action on member (start chat, mention) + self.spaceMemberDetailViewController.delegate = self + self.spaceMemberDetailViewController.display(self.member, withMatrixRoom: space.room) + } + } + + func toPresentable() -> UIViewController { + return self.spaceMemberDetailViewController + } +} + +// MARK: - SpaceMemberDetailViewModelCoordinatorDelegate +extension SpaceMemberDetailCoordinator: SpaceMemberDetailViewModelCoordinatorDelegate { + + func spaceMemberDetailViewModel(_ viewModel: SpaceMemberDetailViewModelType, showRoomWithId roomId: String) { + self.delegate?.spaceMemberDetailCoordinator(self, showRoomWithId: roomId) + } + + func spaceMemberDetailViewModelDidCancel(_ viewModel: SpaceMemberDetailViewModelType) { + self.delegate?.spaceMemberDetailCoordinatorDidCancel(self) + } + +} + +// MARK: - MXKRoomMemberDetailsViewControllerDelegate +extension SpaceMemberDetailCoordinator: MXKRoomMemberDetailsViewControllerDelegate { + + func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, startChatWithMemberId memberId: String!, completion: (() -> Void)!) { + completion() + self.spaceMemberDetailViewModel.process(viewAction: .createRoom(memberId)) + } + +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinatorType.swift similarity index 55% rename from Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinatorType.swift index 2c6de6e975..f1e09ec84b 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailCoordinatorType.swift @@ -18,12 +18,12 @@ import Foundation -protocol ShowSpaceMemberDetailCoordinatorDelegate: AnyObject { - func showSpaceMemberDetailCoordinator(_ coordinator: ShowSpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) - func showSpaceMemberDetailCoordinatorDidCancel(_ coordinator: ShowSpaceMemberDetailCoordinatorType) +protocol SpaceMemberDetailCoordinatorDelegate: AnyObject { + func spaceMemberDetailCoordinator(_ coordinator: SpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) + func spaceMemberDetailCoordinatorDidCancel(_ coordinator: SpaceMemberDetailCoordinatorType) } -/// `ShowSpaceMemberDetailCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. -protocol ShowSpaceMemberDetailCoordinatorType: Coordinator, Presentable { - var delegate: ShowSpaceMemberDetailCoordinatorDelegate? { get } +/// `SpaceMemberDetailCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol SpaceMemberDetailCoordinatorType: Coordinator, Presentable { + var delegate: SpaceMemberDetailCoordinatorDelegate? { get } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift similarity index 87% rename from Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift index f13fcbce23..3ea17426e4 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewAction.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift @@ -18,8 +18,8 @@ import Foundation -/// ShowSpaceMemberDetailViewController view actions exposed to view model -enum ShowSpaceMemberDetailViewAction { +/// SpaceMemberDetailViewController view actions exposed to view model +enum SpaceMemberDetailViewAction { case openRoom(_ roomId: String) case createRoom(_ memberId: String) case cancel diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift similarity index 86% rename from Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift index a0596a70ba..ee9a65b452 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift @@ -18,7 +18,7 @@ import UIKit -final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController { +final class SpaceMemberDetailViewController: RoomMemberDetailsViewController { // MARK: - Constants @@ -36,7 +36,7 @@ final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController // MARK: Private - private var viewModel: ShowSpaceMemberDetailViewModelType! + private var viewModel: SpaceMemberDetailViewModelType! private var theme: Theme! private var keyboardAvoider: KeyboardAvoider? private var errorPresenter: MXKErrorPresentation! @@ -44,8 +44,8 @@ final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController // MARK: - Setup - class func instantiate(with viewModel: ShowSpaceMemberDetailViewModelType) -> ShowSpaceMemberDetailViewController { - let viewController = ShowSpaceMemberDetailViewController() + class func instantiate(with viewModel: SpaceMemberDetailViewModelType) -> SpaceMemberDetailViewController { + let viewController = SpaceMemberDetailViewController() viewController.viewModel = viewModel viewController.theme = ThemeService.shared().theme return viewController @@ -114,7 +114,7 @@ final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController self.navigationItem.rightBarButtonItem = cancelBarButtonItem } - private func render(viewState: ShowSpaceMemberDetailViewState) { + private func render(viewState: SpaceMemberDetailViewState) { switch viewState { case .loading: self.renderLoading() @@ -152,10 +152,10 @@ final class ShowSpaceMemberDetailViewController: RoomMemberDetailsViewController } -// MARK: - ShowSpaceMemberDetailViewModelViewDelegate -extension ShowSpaceMemberDetailViewController: ShowSpaceMemberDetailViewModelViewDelegate { +// MARK: - SpaceMemberDetailViewModelViewDelegate +extension SpaceMemberDetailViewController: SpaceMemberDetailViewModelViewDelegate { - func showSpaceMemberDetailViewModel(_ viewModel: ShowSpaceMemberDetailViewModelType, didUpdateViewState viewSate: ShowSpaceMemberDetailViewState) { + func spaceMemberDetailViewModel(_ viewModel: SpaceMemberDetailViewModelType, didUpdateViewState viewSate: SpaceMemberDetailViewState) { self.render(viewState: viewSate) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift similarity index 82% rename from Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift index 5ee184369f..f18ad137e1 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift @@ -18,7 +18,7 @@ import Foundation -final class ShowSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType { +final class SpaceMemberDetailViewModel: SpaceMemberDetailViewModelType { // MARK: - Properties @@ -31,8 +31,8 @@ final class ShowSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType { // MARK: Public - weak var viewDelegate: ShowSpaceMemberDetailViewModelViewDelegate? - weak var coordinatorDelegate: ShowSpaceMemberDetailViewModelCoordinatorDelegate? + weak var viewDelegate: SpaceMemberDetailViewModelViewDelegate? + weak var coordinatorDelegate: SpaceMemberDetailViewModelCoordinatorDelegate? // MARK: - Setup @@ -47,22 +47,22 @@ final class ShowSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType { // MARK: - Public - func process(viewAction: ShowSpaceMemberDetailViewAction) { + func process(viewAction: SpaceMemberDetailViewAction) { switch viewAction { case .openRoom(let roomId): - self.coordinatorDelegate?.showSpaceMemberDetailViewModel(self, showRoomWithId: roomId) + self.coordinatorDelegate?.spaceMemberDetailViewModel(self, showRoomWithId: roomId) case .createRoom(let memberId): self.createDirectRoom(forMemberWithId: memberId) case .cancel: self.cancelOperations() - self.coordinatorDelegate?.showSpaceMemberDetailViewModelDidCancel(self) + self.coordinatorDelegate?.spaceMemberDetailViewModelDidCancel(self) } } // MARK: - Private - private func update(viewState: ShowSpaceMemberDetailViewState) { - self.viewDelegate?.showSpaceMemberDetailViewModel(self, didUpdateViewState: viewState) + private func update(viewState: SpaceMemberDetailViewState) { + self.viewDelegate?.spaceMemberDetailViewModel(self, didUpdateViewState: viewState) } private func createDirectRoom(forMemberWithId memberId: String) { @@ -95,7 +95,7 @@ final class ShowSpaceMemberDetailViewModel: ShowSpaceMemberDetailViewModelType { } return } - self.coordinatorDelegate?.showSpaceMemberDetailViewModel(self, showRoomWithId: room.roomId) + self.coordinatorDelegate?.spaceMemberDetailViewModel(self, showRoomWithId: room.roomId) } } failure: { error in self.update(viewState: .loaded) diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModelType.swift new file mode 100644 index 0000000000..86d7e8b7d2 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberDetail ShowSpaceMemberDetail +/* + 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 SpaceMemberDetailViewModelViewDelegate: AnyObject { + func spaceMemberDetailViewModel(_ viewModel: SpaceMemberDetailViewModelType, didUpdateViewState viewSate: SpaceMemberDetailViewState) +} + +protocol SpaceMemberDetailViewModelCoordinatorDelegate: AnyObject { + func spaceMemberDetailViewModel(_ viewModel: SpaceMemberDetailViewModelType, showRoomWithId roomId: String) + func spaceMemberDetailViewModelDidCancel(_ viewModel: SpaceMemberDetailViewModelType) +} + +/// Protocol describing the view model used by `SpaceMemberDetailViewController` +protocol SpaceMemberDetailViewModelType { + + var viewDelegate: SpaceMemberDetailViewModelViewDelegate? { get set } + var coordinatorDelegate: SpaceMemberDetailViewModelCoordinatorDelegate? { get set } + + func process(viewAction: SpaceMemberDetailViewAction) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift similarity index 89% rename from Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift index 5d81fd2746..db35093fa6 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/ShowSpaceMemberDetailViewState.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift @@ -18,8 +18,8 @@ import Foundation -/// ShowSpaceMemberDetailViewController view state -enum ShowSpaceMemberDetailViewState { +/// SpaceMemberDetailViewController view state +enum SpaceMemberDetailViewState { case loading case loaded case error(Error) diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift deleted file mode 100644 index 6ea9ecaf94..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinator.swift +++ /dev/null @@ -1,73 +0,0 @@ -// File created from ScreenTemplate -// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList -/* - 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 ShowSpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorType { - - // MARK: - Properties - - // MARK: Private - - private let session: MXSession - private let spaceId: String - private var showSpaceMemberListViewModel: ShowSpaceMemberListViewModelType - private let showSpaceMemberListViewController: ShowSpaceMemberListViewController - - // MARK: Public - - // Must be used only internally - var childCoordinators: [Coordinator] = [] - - weak var delegate: ShowSpaceMemberListCoordinatorDelegate? - - // MARK: - Setup - - init(session: MXSession, spaceId: String) { - self.session = session - self.spaceId = spaceId - - let showSpaceMemberListViewModel = ShowSpaceMemberListViewModel(session: self.session, spaceId: self.spaceId) - let showSpaceMemberListViewController = ShowSpaceMemberListViewController.instantiate(with: showSpaceMemberListViewModel) - self.showSpaceMemberListViewModel = showSpaceMemberListViewModel - self.showSpaceMemberListViewController = showSpaceMemberListViewController - } - - // MARK: - Public methods - - func start() { - self.showSpaceMemberListViewModel.coordinatorDelegate = self - } - - func toPresentable() -> UIViewController { - return self.showSpaceMemberListViewController - } -} - -// MARK: - ShowSpaceMemberListViewModelCoordinatorDelegate -extension ShowSpaceMemberListCoordinator: ShowSpaceMemberListViewModelCoordinatorDelegate { - - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) { - self.delegate?.showSpaceMemberListCoordinator(self, didSelect: member, from: sourceView) - } - - func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) { - self.delegate?.showSpaceMemberListCoordinatorDidCancel(self) - } -} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift deleted file mode 100644 index 831289929d..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListCoordinatorType.swift +++ /dev/null @@ -1,29 +0,0 @@ -// File created from ScreenTemplate -// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList -/* - 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 ShowSpaceMemberListCoordinatorDelegate: AnyObject { - func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) - func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) -} - -/// `ShowSpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. -protocol ShowSpaceMemberListCoordinatorType: Coordinator, Presentable { - var delegate: ShowSpaceMemberListCoordinatorDelegate? { get } -} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift deleted file mode 100644 index 7ab08f313a..0000000000 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModelType.swift +++ /dev/null @@ -1,37 +0,0 @@ -// File created from ScreenTemplate -// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList -/* - 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 ShowSpaceMemberListViewModelViewDelegate: AnyObject { - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didUpdateViewState viewSate: ShowSpaceMemberListViewState) -} - -protocol ShowSpaceMemberListViewModelCoordinatorDelegate: AnyObject { - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) - func showSpaceMemberListViewModelDidCancel(_ viewModel: ShowSpaceMemberListViewModelType) -} - -/// Protocol describing the view model used by `ShowSpaceMemberListViewController` -protocol ShowSpaceMemberListViewModelType { - - var viewDelegate: ShowSpaceMemberListViewModelViewDelegate? { get set } - var coordinatorDelegate: ShowSpaceMemberListViewModelCoordinatorDelegate? { get set } - - func process(viewAction: ShowSpaceMemberListViewAction) -} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinator.swift new file mode 100644 index 0000000000..77ec12f624 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinator.swift @@ -0,0 +1,73 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let spaceId: String + private var spaceMemberListViewModel: SpaceMemberListViewModelType + private let spaceMemberListViewController: SpaceMemberListViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: SpaceMemberListCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, spaceId: String) { + self.session = session + self.spaceId = spaceId + + let spaceMemberListViewModel = SpaceMemberListViewModel(session: self.session, spaceId: self.spaceId) + let spaceMemberListViewController = SpaceMemberListViewController.instantiate(with: spaceMemberListViewModel) + self.spaceMemberListViewModel = spaceMemberListViewModel + self.spaceMemberListViewController = spaceMemberListViewController + } + + // MARK: - Public methods + + func start() { + self.spaceMemberListViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.spaceMemberListViewController + } +} + +// MARK: - SpaceMemberListViewModelCoordinatorDelegate +extension SpaceMemberListCoordinator: SpaceMemberListViewModelCoordinatorDelegate { + + func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.delegate?.spaceMemberListCoordinator(self, didSelect: member, from: sourceView) + } + + func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType) { + self.delegate?.spaceMemberListCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinatorType.swift similarity index 85% rename from Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinatorType.swift index 228ad50827..43225f787d 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListCoordinatorType.swift @@ -1,5 +1,5 @@ -// File created from FlowTemplate -// $ createRootCoordinator.sh Spaces/SpaceMembers SpaceMemberList ShowSpaceMemberList +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList /* Copyright 2021 New Vector Ltd @@ -19,11 +19,11 @@ import Foundation protocol SpaceMemberListCoordinatorDelegate: AnyObject { - func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) + func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) } -/// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +/// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. protocol SpaceMemberListCoordinatorType: Coordinator, Presentable { var delegate: SpaceMemberListCoordinatorDelegate? { get } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewAction.swift similarity index 88% rename from Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewAction.swift index 33c6025348..83118ce295 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewAction.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewAction.swift @@ -18,8 +18,8 @@ import Foundation -/// ShowSpaceMemberListViewController view actions exposed to view model -enum ShowSpaceMemberListViewAction { +/// SpaceMemberListViewController view actions exposed to view model +enum SpaceMemberListViewAction { case loadData case complete(_ selectedMember: MXRoomMember, _ sourceView: UIView?) case cancel diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift similarity index 90% rename from Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift index 6eff35eef2..adc2ee6f08 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift @@ -18,7 +18,7 @@ import UIKit -final class ShowSpaceMemberListViewController: RoomParticipantsViewController { +final class SpaceMemberListViewController: RoomParticipantsViewController { // MARK: - Constants @@ -26,7 +26,7 @@ final class ShowSpaceMemberListViewController: RoomParticipantsViewController { // MARK: Private - private var viewModel: ShowSpaceMemberListViewModelType! + private var viewModel: SpaceMemberListViewModelType! private var theme: Theme! private var errorPresenter: MXKErrorPresentation! private var activityPresenter: ActivityIndicatorPresenter! @@ -39,8 +39,8 @@ final class ShowSpaceMemberListViewController: RoomParticipantsViewController { // MARK: - Setup - class func instantiate(with viewModel: ShowSpaceMemberListViewModelType) -> ShowSpaceMemberListViewController { - let viewController = ShowSpaceMemberListViewController() + class func instantiate(with viewModel: SpaceMemberListViewModelType) -> SpaceMemberListViewController { + let viewController = SpaceMemberListViewController() viewController.viewModel = viewModel viewController.theme = ThemeService.shared().theme viewController.emptyView = RootTabEmptyView.instantiate() @@ -113,7 +113,7 @@ final class ShowSpaceMemberListViewController: RoomParticipantsViewController { self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.spacesNoResultFoundTitle, informationText: VectorL10n.spacesNoMemberFoundDetail) } - private func render(viewState: ShowSpaceMemberListViewState) { + private func render(viewState: SpaceMemberListViewState) { switch viewState { case .loading: self.renderLoading() @@ -185,10 +185,10 @@ final class ShowSpaceMemberListViewController: RoomParticipantsViewController { } -// MARK: - ShowSpaceMemberListViewModelViewDelegate -extension ShowSpaceMemberListViewController: ShowSpaceMemberListViewModelViewDelegate { +// MARK: - SpaceMemberListViewModelViewDelegate +extension SpaceMemberListViewController: SpaceMemberListViewModelViewDelegate { - func showSpaceMemberListViewModel(_ viewModel: ShowSpaceMemberListViewModelType, didUpdateViewState viewSate: ShowSpaceMemberListViewState) { + func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didUpdateViewState viewSate: SpaceMemberListViewState) { self.render(viewState: viewSate) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModel.swift similarity index 72% rename from Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModel.swift index ffc078e3ff..c8d7ec1fdf 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModel.swift @@ -18,7 +18,7 @@ import Foundation -final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { +final class SpaceMemberListViewModel: SpaceMemberListViewModelType { // MARK: - Properties @@ -32,8 +32,8 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { // MARK: Public - weak var viewDelegate: ShowSpaceMemberListViewModelViewDelegate? - weak var coordinatorDelegate: ShowSpaceMemberListViewModelCoordinatorDelegate? + weak var viewDelegate: SpaceMemberListViewModelViewDelegate? + weak var coordinatorDelegate: SpaceMemberListViewModelCoordinatorDelegate? // MARK: - Setup @@ -48,15 +48,15 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { // MARK: - Public - func process(viewAction: ShowSpaceMemberListViewAction) { + func process(viewAction: SpaceMemberListViewAction) { switch viewAction { case .loadData: self.loadData() case .complete(let selectedMember, let sourceView): - self.coordinatorDelegate?.showSpaceMemberListViewModel(self, didSelect: selectedMember, from: sourceView) + self.coordinatorDelegate?.spaceMemberListViewModel(self, didSelect: selectedMember, from: sourceView) case .cancel: self.cancelOperations() - self.coordinatorDelegate?.showSpaceMemberListViewModelDidCancel(self) + self.coordinatorDelegate?.spaceMemberListViewModelDidCancel(self) } } @@ -68,8 +68,8 @@ final class ShowSpaceMemberListViewModel: ShowSpaceMemberListViewModelType { } } - private func update(viewState: ShowSpaceMemberListViewState) { - self.viewDelegate?.showSpaceMemberListViewModel(self, didUpdateViewState: viewState) + private func update(viewState: SpaceMemberListViewState) { + self.viewDelegate?.spaceMemberListViewModel(self, didUpdateViewState: viewState) } private func cancelOperations() { diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModelType.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModelType.swift new file mode 100644 index 0000000000..a07ceb55ea --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Spaces/SpaceMembers/MemberList ShowSpaceMemberList +/* + 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 SpaceMemberListViewModelViewDelegate: AnyObject { + func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didUpdateViewState viewSate: SpaceMemberListViewState) +} + +protocol SpaceMemberListViewModelCoordinatorDelegate: AnyObject { + func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) + func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType) +} + +/// Protocol describing the view model used by `SpaceMemberListViewController` +protocol SpaceMemberListViewModelType { + + var viewDelegate: SpaceMemberListViewModelViewDelegate? { get set } + var coordinatorDelegate: SpaceMemberListViewModelCoordinatorDelegate? { get set } + + func process(viewAction: SpaceMemberListViewAction) +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewState.swift similarity index 90% rename from Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift rename to Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewState.swift index fd8ad8dda0..407060c407 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/ShowSpaceMemberListViewState.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewState.swift @@ -18,8 +18,8 @@ import Foundation -/// ShowSpaceMemberListViewController view state -enum ShowSpaceMemberListViewState { +/// SpaceMemberListViewController view state +enum SpaceMemberListViewState { case loading case loaded(_ space: MXSpace) case error(Error) diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift similarity index 70% rename from Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift rename to Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift index 4731df629c..42fee44b72 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift @@ -19,7 +19,7 @@ import UIKit @objcMembers -final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { +final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { // MARK: - Properties @@ -28,14 +28,14 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { private let navigationRouter: NavigationRouterType private let session: MXSession private let spaceId: String - private weak var memberDetailCoordinator: ShowSpaceMemberDetailCoordinator? + private weak var memberDetailCoordinator: SpaceMemberDetailCoordinator? // MARK: Public // Must be used only internally var childCoordinators: [Coordinator] = [] - weak var delegate: SpaceMemberListCoordinatorDelegate? + weak var delegate: SpaceMembersCoordinatorDelegate? // MARK: - Setup @@ -49,7 +49,7 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { func start() { - let rootCoordinator = self.createShowSpaceMemberListCoordinator() + let rootCoordinator = self.createSpaceMemberListCoordinator() rootCoordinator.start() @@ -63,7 +63,7 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { } func presentMemberDetail(with member: MXRoomMember, from sourceView: UIView?) { - let coordinator = self.createShowSpaceMemberDetailCoordinator(with: member) + let coordinator = self.createSpaceMemberDetailCoordinator(with: member) coordinator.start() self.add(childCoordinator: coordinator) self.memberDetailCoordinator = coordinator @@ -88,14 +88,14 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { // MARK: - Private methods - private func createShowSpaceMemberListCoordinator() -> ShowSpaceMemberListCoordinator { - let coordinator = ShowSpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) + private func createSpaceMemberListCoordinator() -> SpaceMemberListCoordinator { + let coordinator = SpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) coordinator.delegate = self return coordinator } - private func createShowSpaceMemberDetailCoordinator(with member: MXRoomMember) -> ShowSpaceMemberDetailCoordinator { - let coordinator = ShowSpaceMemberDetailCoordinator(session: self.session, member: member, spaceId: self.spaceId) + private func createSpaceMemberDetailCoordinator(with member: MXRoomMember) -> SpaceMemberDetailCoordinator { + let coordinator = SpaceMemberDetailCoordinator(session: self.session, member: member, spaceId: self.spaceId) coordinator.delegate = self return coordinator } @@ -117,19 +117,19 @@ final class SpaceMemberListCoordinator: SpaceMemberListCoordinatorType { } } -// MARK: - ShowSpaceMemberListCoordinatorDelegate -extension SpaceMemberListCoordinator: ShowSpaceMemberListCoordinatorDelegate { - func showSpaceMemberListCoordinator(_ coordinator: ShowSpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { - self.delegate?.spaceMemberListCoordinator(self, didSelect: member, from: sourceView) +// MARK: - SpaceMemberListCoordinatorDelegate +extension SpaceMembersCoordinator: SpaceMemberListCoordinatorDelegate { + func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { + self.delegate?.spaceMembersCoordinator(self, didSelect: member, from: sourceView) } - func showSpaceMemberListCoordinatorDidCancel(_ coordinator: ShowSpaceMemberListCoordinatorType) { - self.delegate?.spaceMemberListCoordinatorDidCancel(self) + func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) { + self.delegate?.spaceMembersCoordinatorDidCancel(self) } } -extension SpaceMemberListCoordinator: ShowSpaceMemberDetailCoordinatorDelegate { - func showSpaceMemberDetailCoordinator(_ coordinator: ShowSpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) { +extension SpaceMembersCoordinator: SpaceMemberDetailCoordinatorDelegate { + func spaceMemberDetailCoordinator(_ coordinator: SpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) { if !UIDevice.current.isPhone, let memberDetailCoordinator = self.memberDetailCoordinator { memberDetailCoordinator.toPresentable().dismiss(animated: true, completion: { self.memberDetailCoordinator = nil @@ -140,7 +140,7 @@ extension SpaceMemberListCoordinator: ShowSpaceMemberDetailCoordinatorDelegate { } } - func showSpaceMemberDetailCoordinatorDidCancel(_ coordinator: ShowSpaceMemberDetailCoordinatorType) { - self.delegate?.spaceMemberListCoordinatorDidCancel(self) + func spaceMemberDetailCoordinatorDidCancel(_ coordinator: SpaceMemberDetailCoordinatorType) { + self.delegate?.spaceMembersCoordinatorDidCancel(self) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift similarity index 64% rename from Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift rename to Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift index f65b04e267..b234541bbe 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMemberListCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift @@ -18,15 +18,15 @@ import Foundation -@objc protocol SpaceMemberListCoordinatorBridgePresenterDelegate { - func spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SpaceMemberListCoordinatorBridgePresenter) +@objc protocol SpaceMembersCoordinatorBridgePresenterDelegate { + func spaceMembersCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SpaceMembersCoordinatorBridgePresenter) } -/// SpaceMemberListCoordinatorBridgePresenter enables to start SpaceMemberListCoordinator from a view controller. +/// SpaceMembersCoordinatorBridgePresenter enables to start SpaceMemberListCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. /// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (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 SpaceMemberListCoordinatorBridgePresenter: NSObject { +final class SpaceMembersCoordinatorBridgePresenter: NSObject { // MARK: - Properties @@ -34,11 +34,11 @@ final class SpaceMemberListCoordinatorBridgePresenter: NSObject { private let session: MXSession private let spaceId: String - private var coordinator: SpaceMemberListCoordinator? + private var coordinator: SpaceMembersCoordinator? // MARK: Public - weak var delegate: SpaceMemberListCoordinatorBridgePresenterDelegate? + weak var delegate: SpaceMembersCoordinatorBridgePresenterDelegate? // MARK: - Setup @@ -51,7 +51,7 @@ final class SpaceMemberListCoordinatorBridgePresenter: NSObject { // MARK: - Public func present(from viewController: UIViewController, animated: Bool) { - let spaceMemberListCoordinator = SpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) + let spaceMemberListCoordinator = SpaceMembersCoordinator(session: self.session, spaceId: self.spaceId) spaceMemberListCoordinator.delegate = self let presentable = spaceMemberListCoordinator.toPresentable() presentable.presentationController?.delegate = self @@ -81,23 +81,23 @@ final class SpaceMemberListCoordinatorBridgePresenter: NSObject { } } -// MARK: - SpaceMemberListCoordinatorDelegate -extension SpaceMemberListCoordinatorBridgePresenter: SpaceMemberListCoordinatorDelegate { - func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) { - self.delegate?.spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(self) +// MARK: - SpaceMembersCoordinatorDelegate +extension SpaceMembersCoordinatorBridgePresenter: SpaceMembersCoordinatorDelegate { + func spaceMembersCoordinatorDidCancel(_ coordinator: SpaceMembersCoordinatorType) { + self.delegate?.spaceMembersCoordinatorBridgePresenterDelegateDidComplete(self) } - func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { + func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { self.navigate(to: member, from: sourceView) } } // MARK: - UIAdaptivePresentationControllerDelegate -extension SpaceMemberListCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { +extension SpaceMembersCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { - func spaceMemberListCoordinatorDidComplete(_ presentationController: UIPresentationController) { - self.delegate?.spaceMemberListCoordinatorBridgePresenterDelegateDidComplete(self) + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.delegate?.spaceMembersCoordinatorBridgePresenterDelegateDidComplete(self) } } diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift new file mode 100644 index 0000000000..2333fbb378 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Spaces/SpaceMembers SpaceMemberList ShowSpaceMemberList +/* + 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 SpaceMembersCoordinatorDelegate: AnyObject { + func spaceMembersCoordinatorDidCancel(_ coordinator: SpaceMembersCoordinatorType) + func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) +} + +/// `SpaceMembersCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol SpaceMembersCoordinatorType: Coordinator, Presentable { + var delegate: SpaceMembersCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift index d456656a83..1bcf31afff 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift @@ -104,7 +104,7 @@ extension ExploreRoomCoordinatorBridgePresenter: ExploreRoomCoordinatorDelegate // MARK: - UIAdaptivePresentationControllerDelegate extension ExploreRoomCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { - func exploreRoomCoordinatorDidComplete(_ presentationController: UIPresentationController) { + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { self.delegate?.exploreRoomCoordinatorBridgePresenterDelegateDidComplete(self) } From e46f5faec3b78f60fe07da4f2b6b60e7fb6d7228 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 2 Sep 2021 10:17:09 +0300 Subject: [PATCH 05/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after design review --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 6 +++--- .../MemberList/SpaceMemberListViewController.swift | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a7422f83a4..add2164d1d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1682,7 +1682,7 @@ Tap the + to start adding people."; "spaces_empty_space_detail" = "Some rooms may be hidden because they’re private and you need an invite."; "spaces_no_result_found_title" = "No results found"; "spaces_no_room_found_detail" = "Some results may be hidden because they’re private and you need an invite to join them."; -"spaces_no_member_found_detail" = "Looking for someone not in space name? For now, you can invite them on web or desktop."; +"spaces_no_member_found_detail" = "Looking for someone not in %@? For now, you can invite them on web or desktop."; "spaces_coming_soon_title" = "Coming soon"; "spaces_coming_soon_detail" = "This feature hasn’t been implemented here, but it’s on the way. For now, you can do that with Element on your computer."; "space_participants_action_remove" = "Remove from this space"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2f321b2f48..9111ce6558 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4762,9 +4762,9 @@ internal enum VectorL10n { internal static var spacesLeftPanelTitle: String { return VectorL10n.tr("Vector", "spaces_left_panel_title") } - /// Looking for someone not in space name? For now, you can invite them on web or desktop. - internal static var spacesNoMemberFoundDetail: String { - return VectorL10n.tr("Vector", "spaces_no_member_found_detail") + /// Looking for someone not in %@? For now, you can invite them on web or desktop. + internal static func spacesNoMemberFoundDetail(_ p1: String) -> String { + return VectorL10n.tr("Vector", "spaces_no_member_found_detail", p1) } /// No results found internal static var spacesNoResultFoundTitle: String { diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift index adc2ee6f08..e4b6b7dd73 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift @@ -85,6 +85,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { theme.applyStyle(onSearchBar: self.searchBarView) self.titleView.update(theme: theme) + self.emptyView.update(theme: theme) } private func registerThemeServiceDidChangeThemeNotification() { @@ -110,7 +111,6 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { self.emptyView.autoresizingMask = [.flexibleHeight, .flexibleWidth] self.emptyView.alpha = 0 self.view.insertSubview(self.emptyView, at: 0) - self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.spacesNoResultFoundTitle, informationText: VectorL10n.spacesNoMemberFoundDetail) } private func render(viewState: SpaceMemberListViewState) { @@ -132,6 +132,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { self.activityPresenter.removeCurrentActivityIndicator(animated: true) self.mxRoom = space.room self.titleView.subtitleLabel.text = space.summary?.displayname + self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.spacesNoResultFoundTitle, informationText: VectorL10n.spacesNoMemberFoundDetail(space.summary?.displayname ?? "")) } private func render(error: Error) { From be960960db1d6f05c7ffc392c4210921efdcea9d Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 2 Sep 2021 11:24:07 +0300 Subject: [PATCH 06/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after design review --- Riot/Modules/Contacts/Views/ContactTableViewCell.m | 11 +---------- .../Room/Members/RoomParticipantsViewController.h | 1 + .../Room/Members/RoomParticipantsViewController.m | 2 ++ .../MemberList/SpaceMemberListViewController.swift | 1 + 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Contacts/Views/ContactTableViewCell.m b/Riot/Modules/Contacts/Views/ContactTableViewCell.m index cf3ec0a775..a22454161e 100644 --- a/Riot/Modules/Contacts/Views/ContactTableViewCell.m +++ b/Riot/Modules/Contacts/Views/ContactTableViewCell.m @@ -86,16 +86,7 @@ - (void)setShowCustomAccessoryView:(BOOL)show { _showCustomAccessoryView = show; - if (show) - { - self.customAccessViewWidthConstraint.constant = 25; - self.customAccessoryViewLeadingConstraint.constant = 13; - } - else - { - self.customAccessViewWidthConstraint.constant = 0; - self.customAccessoryViewLeadingConstraint.constant = 0; - } + self.customAccessViewWidthConstraint.constant = show ? 25 : 0; } - (void)setShowMatrixIdInDisplayName:(BOOL)showMatrixIdInDisplayName diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.h b/Riot/Modules/Room/Members/RoomParticipantsViewController.h index 01ee41929a..c0b8dc3f8e 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.h +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.h @@ -85,6 +85,7 @@ @property (nonatomic) BOOL enableMention; @property (nonatomic) BOOL showCancelBarButtonItem; +@property (nonatomic) BOOL showParticipantCustomAccessoryView; /** The delegate for the view controller. diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.m b/Riot/Modules/Room/Members/RoomParticipantsViewController.m index 8d370c58da..2d84fa5955 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.m +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.m @@ -84,6 +84,7 @@ - (void)finalizeInit // Setup `MXKViewControllerHandling` properties self.enableBarTintColorStatusChange = NO; self.rageShakeManager = [RageShakeManager sharedManager]; + self.showParticipantCustomAccessoryView = YES; } - (void)viewDidLoad @@ -969,6 +970,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N { ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:@"ParticipantTableViewCellId" forIndexPath:indexPath]; participantCell.selectionStyle = UITableViewCellSelectionStyleNone; + participantCell.showCustomAccessoryView = self.showParticipantCustomAccessoryView; participantCell.mxRoom = self.mxRoom; diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift index e4b6b7dd73..baad3654f8 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift @@ -42,6 +42,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { class func instantiate(with viewModel: SpaceMemberListViewModelType) -> SpaceMemberListViewController { let viewController = SpaceMemberListViewController() viewController.viewModel = viewModel + viewController.showParticipantCustomAccessoryView = false viewController.theme = ThemeService.shared().theme viewController.emptyView = RootTabEmptyView.instantiate() return viewController From 652d71b2e230cb4e57ef84839545f3e2d2603697 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 13 Sep 2021 16:03:56 +0200 Subject: [PATCH 07/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after design review --- .../SideMenu/SideMenuCoordinator.swift | 8 --- .../MemberList/SeachEmptyView.swift | 67 +++++++++++++++++++ .../SpaceMemberListViewController.swift | 14 ++-- .../SpaceMembersCoordinator.swift | 2 +- ...aceMembersCoordinatorBridgePresenter.swift | 10 --- .../SpaceMembersCoordinatorType.swift | 1 - 6 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 Riot/Modules/Spaces/SpaceMembers/MemberList/SeachEmptyView.swift diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index d4f350c9a4..8e1ff25321 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -243,10 +243,6 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { self.spaceMenuPresenter.present(forSpaceWithId: spaceId, from: self.sideMenuViewController, sourceView: sourceView, session: session, animated: true) } - func navigate(to member: MXRoomMember, from sourceView: UIView?) { - self.membersCoordinator?.presentMemberDetail(with: member, from: sourceView) - } - // MARK: UserSessions management private func registerUserSessionsServiceNotifications() { @@ -349,10 +345,6 @@ extension SideMenuCoordinator: SpaceMembersCoordinatorDelegate { self.membersCoordinator = nil } } - - func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { - self.navigate(to: member, from: sourceView) - } } // MARK: - UIAdaptivePresentationControllerDelegate diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SeachEmptyView.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SeachEmptyView.swift new file mode 100644 index 0000000000..06982a6459 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SeachEmptyView.swift @@ -0,0 +1,67 @@ +// +// 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 + +@objcMembers +class SearchEmptyView: UIStackView, Themable { + + // MARK: - Properties + + public private(set) var titleLabel: UILabel! + public private(set) var detailLabel: UILabel! + + // MARK: - Lifecycle + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + required init(coder: NSCoder) { + super.init(coder: coder) + setupView() + } + + // MARK: - Themable + + func update(theme: Theme) { + self.titleLabel.textColor = theme.colors.primaryContent + self.titleLabel.font = theme.fonts.bodySB + + self.detailLabel.textColor = theme.colors.secondaryContent + self.detailLabel.font = theme.fonts.callout + } + + // MARK: - Private + + private func setupView() { + self.titleLabel = UILabel(frame: .zero) + self.titleLabel.backgroundColor = .clear + self.titleLabel.numberOfLines = 0 + + self.detailLabel = UILabel(frame: .zero) + self.detailLabel.backgroundColor = .clear + self.detailLabel.numberOfLines = 0 + + self.addArrangedSubview(titleLabel) + self.addArrangedSubview(detailLabel) + self.distribution = .equalSpacing + self.axis = .vertical + self.alignment = .leading + self.spacing = 8 + } +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift index baad3654f8..461a164507 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberList/SpaceMemberListViewController.swift @@ -22,6 +22,10 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { // MARK: - Constants + private enum Constants { + static let emptySearchViewMargin: CGFloat = 8 + } + // MARK: - Properties // MARK: Private @@ -31,7 +35,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { private var errorPresenter: MXKErrorPresentation! private var activityPresenter: ActivityIndicatorPresenter! private var titleView: MainTitleView! - private var emptyView: RootTabEmptyView! + private var emptyView: SearchEmptyView! private var emptyViewArtwork: UIImage { return ThemeService.shared().isCurrentThemeDark() ? Asset.Images.peopleEmptyScreenArtworkDark.image : Asset.Images.peopleEmptyScreenArtwork.image @@ -44,7 +48,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { viewController.viewModel = viewModel viewController.showParticipantCustomAccessoryView = false viewController.theme = ThemeService.shared().theme - viewController.emptyView = RootTabEmptyView.instantiate() + viewController.emptyView = SearchEmptyView() return viewController } @@ -108,7 +112,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { self.titleView.titleLabel.text = VectorL10n.roomDetailsPeople self.navigationItem.titleView = self.titleView - self.emptyView.frame = CGRect(x: 0, y: self.searchBarView.frame.maxY, width: self.view.bounds.width, height: self.view.bounds.height - self.searchBarView.frame.maxY) + self.emptyView.frame = CGRect(x: Constants.emptySearchViewMargin, y: self.searchBarView.frame.maxY + 2 * Constants.emptySearchViewMargin, width: self.view.bounds.width - 2 * Constants.emptySearchViewMargin, height: 0) self.emptyView.autoresizingMask = [.flexibleHeight, .flexibleWidth] self.emptyView.alpha = 0 self.view.insertSubview(self.emptyView, at: 0) @@ -133,7 +137,9 @@ final class SpaceMemberListViewController: RoomParticipantsViewController { self.activityPresenter.removeCurrentActivityIndicator(animated: true) self.mxRoom = space.room self.titleView.subtitleLabel.text = space.summary?.displayname - self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.spacesNoResultFoundTitle, informationText: VectorL10n.spacesNoMemberFoundDetail(space.summary?.displayname ?? "")) + self.emptyView.titleLabel.text = VectorL10n.spacesNoResultFoundTitle + self.emptyView.detailLabel.text = VectorL10n.spacesNoMemberFoundDetail(space.summary?.displayname ?? "") + self.emptyView.layoutIfNeeded() } private func render(error: Error) { diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift index 42fee44b72..df0ee1d8d2 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift @@ -120,7 +120,7 @@ final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { // MARK: - SpaceMemberListCoordinatorDelegate extension SpaceMembersCoordinator: SpaceMemberListCoordinatorDelegate { func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { - self.delegate?.spaceMembersCoordinator(self, didSelect: member, from: sourceView) + self.presentMemberDetail(with: member, from: sourceView) } func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) { diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift index b234541bbe..bf5b8c9a9e 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift @@ -73,12 +73,6 @@ final class SpaceMembersCoordinatorBridgePresenter: NSObject { } } } - - // MARK: - Private - - func navigate(to member: MXRoomMember, from sourceView: UIView?) { - self.coordinator?.presentMemberDetail(with: member, from: sourceView) - } } // MARK: - SpaceMembersCoordinatorDelegate @@ -86,10 +80,6 @@ extension SpaceMembersCoordinatorBridgePresenter: SpaceMembersCoordinatorDelegat func spaceMembersCoordinatorDidCancel(_ coordinator: SpaceMembersCoordinatorType) { self.delegate?.spaceMembersCoordinatorBridgePresenterDelegateDidComplete(self) } - - func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) { - self.navigate(to: member, from: sourceView) - } } // MARK: - UIAdaptivePresentationControllerDelegate diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift index 2333fbb378..b2775a3c09 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorType.swift @@ -20,7 +20,6 @@ import Foundation protocol SpaceMembersCoordinatorDelegate: AnyObject { func spaceMembersCoordinatorDidCancel(_ coordinator: SpaceMembersCoordinatorType) - func spaceMembersCoordinator(_ coordinator: SpaceMembersCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) } /// `SpaceMembersCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. From 4da3741140d31893ee492c1de9b33c89a240be41 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 14 Sep 2021 11:45:33 +0200 Subject: [PATCH 08/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after code review --- Riot/Modules/Application/AppCoordinator.swift | 4 +- Riot/Modules/People/PeopleViewController.m | 4 +- .../SideMenu/SideMenuCoordinator.swift | 6 +- .../SpaceMemberDetailCoordinator.swift | 24 +++--- .../SpaceMemberDetailViewAction.swift | 1 + .../SpaceMemberDetailViewController.swift | 24 ++++-- .../SpaceMemberDetailViewModel.swift | 74 +++++++++++-------- .../SpaceMemberDetailViewState.swift | 2 +- .../SpaceMembersCoordinator.swift | 23 +++--- ...aceMembersCoordinatorBridgePresenter.swift | 7 +- 10 files changed, 101 insertions(+), 68 deletions(-) diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index f7b19ec0fd..12f2ef76ea 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -49,7 +49,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { private weak var splitViewCoordinator: SplitViewCoordinatorType? fileprivate weak var sideMenuCoordinator: SideMenuCoordinatorType? - private let userSessionsService: UserSessionsService + let userSessionsService: UserSessionsService /// Main user Matrix session private var mainMatrixSession: MXSession? { @@ -131,7 +131,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { private func addSideMenu() { let appInfo = AppInfo.current - let coordinatorParameters = SideMenuCoordinatorParameters(appNavigator: self.appNavigator, userSessionsService: self.userSessionsService, appInfo: appInfo) + let coordinatorParameters = SideMenuCoordinatorParameters(appNavigator: self.appNavigator, appCoordinator: self, userSessionsService: self.userSessionsService, appInfo: appInfo) let coordinator = SideMenuCoordinator(parameters: coordinatorParameters) coordinator.delegate = self diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index 53bb377bdd..b08b6be8ee 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -30,6 +30,7 @@ @interface PeopleViewController () UIViewController { diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift index 3ea17426e4..98c2ecca1b 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewAction.swift @@ -20,6 +20,7 @@ import Foundation /// SpaceMemberDetailViewController view actions exposed to view model enum SpaceMemberDetailViewAction { + case loadData case openRoom(_ roomId: String) case createRoom(_ memberId: String) case cancel diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift index ee9a65b452..136419f8dd 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewController.swift @@ -26,10 +26,6 @@ final class SpaceMemberDetailViewController: RoomMemberDetailsViewController { return UINib(nibName: "RoomMemberDetailsViewController", bundle: Bundle(for: RoomMemberDetailsViewController.self)) } - private enum Constants { - static let aConstant: Int = 666 - } - // MARK: - Properties // MARK: Outlets @@ -67,6 +63,9 @@ final class SpaceMemberDetailViewController: RoomMemberDetailsViewController { self.update(theme: self.theme) self.viewModel.viewDelegate = self + self.viewModel.process(viewAction: .loadData) + + self.delegate = self } override func viewWillAppear(_ animated: Bool) { @@ -118,8 +117,8 @@ final class SpaceMemberDetailViewController: RoomMemberDetailsViewController { switch viewState { case .loading: self.renderLoading() - case .loaded: - self.renderLoaded() + case .loaded(let member, let space): + self.renderLoaded(member: member, space: space) case .error(let error): self.render(error: error) } @@ -129,7 +128,8 @@ final class SpaceMemberDetailViewController: RoomMemberDetailsViewController { self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) } - private func renderLoaded() { + private func renderLoaded(member: MXRoomMember, space: MXRoom?) { + self.display(member, withMatrixRoom: space) self.activityPresenter.removeCurrentActivityIndicator(animated: true) } @@ -159,3 +159,13 @@ extension SpaceMemberDetailViewController: SpaceMemberDetailViewModelViewDelegat self.render(viewState: viewSate) } } + +// MARK: - MXKRoomMemberDetailsViewControllerDelegate +extension SpaceMemberDetailViewController: MXKRoomMemberDetailsViewControllerDelegate { + + func roomMemberDetailsViewController(_ roomMemberDetailsViewController: MXKRoomMemberDetailsViewController!, startChatWithMemberId memberId: String!, completion: (() -> Void)!) { + completion() + self.viewModel.process(viewAction: .createRoom(memberId)) + } + +} diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift index f18ad137e1..f462dda89d 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewModel.swift @@ -18,14 +18,17 @@ import Foundation -final class SpaceMemberDetailViewModel: SpaceMemberDetailViewModelType { +final class SpaceMemberDetailViewModel: NSObject, SpaceMemberDetailViewModelType { // MARK: - Properties // MARK: Private + private let userSessionsService: UserSessionsService private let session: MXSession private let member: MXRoomMember + private let spaceId: String + private var space: MXSpace? private var currentOperation: MXHTTPOperation? @@ -36,9 +39,11 @@ final class SpaceMemberDetailViewModel: SpaceMemberDetailViewModelType { // MARK: - Setup - init(session: MXSession, member: MXRoomMember) { + init(userSessionsService: UserSessionsService, session: MXSession, member: MXRoomMember, spaceId: String) { + self.userSessionsService = userSessionsService self.session = session self.member = member + self.spaceId = spaceId } deinit { @@ -49,6 +54,8 @@ final class SpaceMemberDetailViewModel: SpaceMemberDetailViewModelType { func process(viewAction: SpaceMemberDetailViewAction) { switch viewAction { + case .loadData: + self.loadData() case .openRoom(let roomId): self.coordinatorDelegate?.spaceMemberDetailViewModel(self, showRoomWithId: roomId) case .createRoom(let memberId): @@ -61,47 +68,50 @@ final class SpaceMemberDetailViewModel: SpaceMemberDetailViewModelType { // MARK: - Private + private func loadData() { + self.space = self.session.spaceService.getSpace(withId: self.spaceId) + self.update(viewState: .loaded(self.member, self.space?.room)) + } + private func update(viewState: SpaceMemberDetailViewState) { self.viewDelegate?.spaceMemberDetailViewModel(self, didUpdateViewState: viewState) } private func createDirectRoom(forMemberWithId memberId: String) { self.update(viewState: .loading) - AppDelegate.theDelegate().selectMatrixAccount { account in - guard let account = account, let session = account.mxSession else { - self.update(viewState: .loaded) - return + guard let account = self.userSessionsService.mainUserSession?.account, let session = account.mxSession else { + self.update(viewState: .loaded(self.member, self.space?.room)) + return + } + + let invite: [String]? = (session.myUserId != memberId) ? [memberId] : nil + self.currentOperation = session.vc_canEnableE2EByDefaultInNewRoom(withUsers: invite) { canEnableE2E in + self.currentOperation = nil + let roomCreationParameters = MXRoomCreationParameters() + roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate + roomCreationParameters.inviteArray = invite + roomCreationParameters.isDirect = !(invite?.isEmpty ?? true) + roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat + + if canEnableE2E { + roomCreationParameters.initialStateEvents = [MXRoomCreationParameters.initialStateEventForEncryption(withAlgorithm: kMXCryptoMegolmAlgorithm)] } - let invite: [String]? = (session.myUserId != memberId) ? [memberId] : nil - self.currentOperation = session.vc_canEnableE2EByDefaultInNewRoom(withUsers: invite) { canEnableE2E in + self.currentOperation = session.createRoom(parameters: roomCreationParameters) { response in self.currentOperation = nil - let roomCreationParameters = MXRoomCreationParameters() - roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate - roomCreationParameters.inviteArray = invite - roomCreationParameters.isDirect = !(invite?.isEmpty ?? true) - roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat - - if canEnableE2E { - roomCreationParameters.initialStateEvents = [MXRoomCreationParameters.initialStateEventForEncryption(withAlgorithm: kMXCryptoMegolmAlgorithm)] - } - - self.currentOperation = session.createRoom(parameters: roomCreationParameters) { response in - self.currentOperation = nil - self.update(viewState: .loaded) - guard response.isSuccess, let room = response.value else { - if let error = response.error { - self.update(viewState: .error(error)) - } - return + self.update(viewState: .loaded(self.member, self.space?.room)) + guard response.isSuccess, let room = response.value else { + if let error = response.error { + self.update(viewState: .error(error)) } - self.coordinatorDelegate?.spaceMemberDetailViewModel(self, showRoomWithId: room.roomId) - } - } failure: { error in - self.update(viewState: .loaded) - if let error = error { - self.update(viewState: .error(error)) + return } + self.coordinatorDelegate?.spaceMemberDetailViewModel(self, showRoomWithId: room.roomId) + } + } failure: { error in + self.update(viewState: .loaded(self.member, self.space?.room)) + if let error = error { + self.update(viewState: .error(error)) } } } diff --git a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift index db35093fa6..e02893efd3 100644 --- a/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift +++ b/Riot/Modules/Spaces/SpaceMembers/MemberDetail/SpaceMemberDetailViewState.swift @@ -21,6 +21,6 @@ import Foundation /// SpaceMemberDetailViewController view state enum SpaceMemberDetailViewState { case loading - case loaded + case loaded(MXRoomMember, MXRoom?) case error(Error) } diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift index df0ee1d8d2..9a0c44ac98 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinator.swift @@ -18,6 +18,12 @@ import UIKit +struct SpaceMembersCoordinatorParameters { + let userSessionsService: UserSessionsService + let session: MXSession + let spaceId: String +} + @objcMembers final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { @@ -25,9 +31,8 @@ final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { // MARK: Private + private let parameters: SpaceMembersCoordinatorParameters private let navigationRouter: NavigationRouterType - private let session: MXSession - private let spaceId: String private weak var memberDetailCoordinator: SpaceMemberDetailCoordinator? // MARK: Public @@ -39,11 +44,10 @@ final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { // MARK: - Setup - init(session: MXSession, spaceId: String) { + init(parameters: SpaceMembersCoordinatorParameters) { + self.parameters = parameters self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) - self.session = session - self.spaceId = spaceId - } + } // MARK: - Public methods @@ -89,19 +93,20 @@ final class SpaceMembersCoordinator: SpaceMembersCoordinatorType { // MARK: - Private methods private func createSpaceMemberListCoordinator() -> SpaceMemberListCoordinator { - let coordinator = SpaceMemberListCoordinator(session: self.session, spaceId: self.spaceId) + let coordinator = SpaceMemberListCoordinator(session: self.parameters.session, spaceId: self.parameters.spaceId) coordinator.delegate = self return coordinator } private func createSpaceMemberDetailCoordinator(with member: MXRoomMember) -> SpaceMemberDetailCoordinator { - let coordinator = SpaceMemberDetailCoordinator(session: self.session, member: member, spaceId: self.spaceId) + let parameters = SpaceMemberDetailCoordinatorParameters(userSessionsService: self.parameters.userSessionsService, member: member, session: self.parameters.session, spaceId: self.parameters.spaceId) + let coordinator = SpaceMemberDetailCoordinator(parameters: parameters) coordinator.delegate = self return coordinator } private func navigateTo(roomWith roomId: String) { - let roomDataSourceManager = MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.session) + let roomDataSourceManager = MXKRoomDataSourceManager.sharedManager(forMatrixSession: self.parameters.session) roomDataSourceManager?.roomDataSource(forRoom: roomId, create: true, onComplete: { [weak self] roomDataSource in let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift index bf5b8c9a9e..7b4ea93c60 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift @@ -32,6 +32,7 @@ final class SpaceMembersCoordinatorBridgePresenter: NSObject { // MARK: Private + private let userSessionsService: UserSessionsService private let session: MXSession private let spaceId: String private var coordinator: SpaceMembersCoordinator? @@ -42,7 +43,8 @@ final class SpaceMembersCoordinatorBridgePresenter: NSObject { // MARK: - Setup - init(session: MXSession, spaceId: String) { + init(userSessionsService: UserSessionsService, session: MXSession, spaceId: String) { + self.userSessionsService = userSessionsService self.session = session self.spaceId = spaceId super.init() @@ -51,7 +53,8 @@ final class SpaceMembersCoordinatorBridgePresenter: NSObject { // MARK: - Public func present(from viewController: UIViewController, animated: Bool) { - let spaceMemberListCoordinator = SpaceMembersCoordinator(session: self.session, spaceId: self.spaceId) + let parameters = SpaceMembersCoordinatorParameters(userSessionsService: self.userSessionsService, session: self.session, spaceId: self.spaceId) + let spaceMemberListCoordinator = SpaceMembersCoordinator(parameters: parameters) spaceMemberListCoordinator.delegate = self let presentable = spaceMemberListCoordinator.toPresentable() presentable.presentationController?.delegate = self From c2e4d915afab955fdb9f7f4ec91265fbc394c829 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 14 Sep 2021 15:29:16 +0200 Subject: [PATCH 09/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after code review --- Riot/Modules/Application/AppCoordinator.swift | 4 ++-- Riot/Modules/SideMenu/SideMenuCoordinator.swift | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index 12f2ef76ea..f7b19ec0fd 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -49,7 +49,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { private weak var splitViewCoordinator: SplitViewCoordinatorType? fileprivate weak var sideMenuCoordinator: SideMenuCoordinatorType? - let userSessionsService: UserSessionsService + private let userSessionsService: UserSessionsService /// Main user Matrix session private var mainMatrixSession: MXSession? { @@ -131,7 +131,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { private func addSideMenu() { let appInfo = AppInfo.current - let coordinatorParameters = SideMenuCoordinatorParameters(appNavigator: self.appNavigator, appCoordinator: self, userSessionsService: self.userSessionsService, appInfo: appInfo) + let coordinatorParameters = SideMenuCoordinatorParameters(appNavigator: self.appNavigator, userSessionsService: self.userSessionsService, appInfo: appInfo) let coordinator = SideMenuCoordinator(parameters: coordinatorParameters) coordinator.delegate = self diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index d58d183a82..1d5230cc61 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -23,16 +23,13 @@ import SafariServices class SideMenuCoordinatorParameters { let appNavigator: AppNavigatorProtocol - let appCoordinator: AppCoordinator let userSessionsService: UserSessionsService let appInfo: AppInfo init(appNavigator: AppNavigatorProtocol, - appCoordinator: AppCoordinator, userSessionsService: UserSessionsService, appInfo: AppInfo) { self.appNavigator = appNavigator - self.appCoordinator = appCoordinator self.userSessionsService = userSessionsService self.appInfo = appInfo } @@ -221,7 +218,7 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { } private func showMembers(spaceId: String, session: MXSession) { - let parameters = SpaceMembersCoordinatorParameters(userSessionsService: parameters.appCoordinator.userSessionsService, session: session, spaceId: spaceId) + let parameters = SpaceMembersCoordinatorParameters(userSessionsService: parameters.userSessionsService, session: session, spaceId: spaceId) let spaceMembersCoordinator = SpaceMembersCoordinator(parameters: parameters) spaceMembersCoordinator.delegate = self let presentable = spaceMembersCoordinator.toPresentable() From f4fb4be6fb4c5ad2056cea33685ce696ffc39bc3 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 14 Sep 2021 16:59:32 +0200 Subject: [PATCH 10/11] [Spaces] M10.8 Browsing users in a space #4682 - Update after code review --- Riot/Managers/UserSessions/UserSessionsService.swift | 4 ++++ Riot/Modules/Application/AppCoordinator.swift | 2 +- Riot/Modules/People/PeopleViewController.m | 4 +--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Riot/Managers/UserSessions/UserSessionsService.swift b/Riot/Managers/UserSessions/UserSessionsService.swift index 19d9b05fa8..f6d91bc0b7 100644 --- a/Riot/Managers/UserSessions/UserSessionsService.swift +++ b/Riot/Managers/UserSessions/UserSessionsService.swift @@ -34,6 +34,10 @@ extension UserSessionsService { @objcMembers class UserSessionsService: NSObject { + // MARK: - Singleton + + static public let shared: UserSessionsService = UserSessionsService() + // MARK: - Properties // MARK: Private diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index f7b19ec0fd..0f62617788 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -67,7 +67,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { init(router: RootRouterType, window: UIWindow) { self.rootRouter = router self.customSchemeURLParser = CustomSchemeURLParser() - self.userSessionsService = UserSessionsService() + self.userSessionsService = UserSessionsService.shared super.init() diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index b08b6be8ee..7d4e9260c0 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -30,7 +30,6 @@ @interface PeopleViewController () Date: Tue, 14 Sep 2021 17:22:48 +0200 Subject: [PATCH 11/11] [Spaces] M10.8 Browsing users in a space #4682 - Fixed build issue on Jenkins --- Riot/Modules/SideMenu/SideMenuCoordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/SideMenu/SideMenuCoordinator.swift b/Riot/Modules/SideMenu/SideMenuCoordinator.swift index 1d5230cc61..fc8c01bd57 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinator.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinator.swift @@ -218,7 +218,7 @@ final class SideMenuCoordinator: NSObject, SideMenuCoordinatorType { } private func showMembers(spaceId: String, session: MXSession) { - let parameters = SpaceMembersCoordinatorParameters(userSessionsService: parameters.userSessionsService, session: session, spaceId: spaceId) + let parameters = SpaceMembersCoordinatorParameters(userSessionsService: self.parameters.userSessionsService, session: session, spaceId: spaceId) let spaceMembersCoordinator = SpaceMembersCoordinator(parameters: parameters) spaceMembersCoordinator.delegate = self let presentable = spaceMembersCoordinator.toPresentable()