diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 1ea613032..d304623cd 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -224,6 +224,8 @@ final class BuildSettings: NSObject { /// Indicates should the app log out the user when number of biometrics failures reaches `maxAllowedNumberOfBiometricsFailures`. Defaults to `false` static let logOutUserWhenBiometricsFailuresExceeded: Bool = false + static let showNotificationsV2: Bool = true + // MARK: - Main Tabs static let homeScreenShowFavouritesTab: Bool = true @@ -298,7 +300,6 @@ final class BuildSettings: NSObject { static let roomSettingsScreenShowFlairSettings: Bool = true static let roomSettingsScreenShowAdvancedSettings: Bool = true static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true - static let roomSettingsScreenShowNotificationsV2: Bool = false // MARK: - Room Member Screen diff --git a/Riot/.DS_Store b/Riot/.DS_Store new file mode 100644 index 000000000..e70071104 Binary files /dev/null and b/Riot/.DS_Store differ diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9731fee82..62d7dd8ac 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1,13 +1,13 @@ /* Copyright 2015 OpenMarket Ltd Copyright 2017 Vector Creations 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. @@ -450,7 +450,7 @@ Tap the + to start adding people."; "settings_config_user_id" = "Logged in as %@"; "settings_user_settings" = "USER SETTINGS"; -"settings_notifications_settings" = "NOTIFICATION SETTINGS"; +"settings_notifications" = "NOTIFICATIONS"; "settings_calls_settings" = "CALLS"; "settings_discovery_settings" = "DISCOVERY"; "settings_identity_server_settings" = "IDENTITY SERVER"; @@ -499,12 +499,10 @@ Tap the + to start adding people."; "settings_pin_rooms_with_unread" = "Pin rooms with unread messages"; "settings_notifications_disabled_alert_title" = "Notifications disabled"; "settings_notifications_disabled_alert_message" = "To enable notifications, go to your device settings."; -//"settings_enable_all_notif" = "Enable all notifications"; -//"settings_messages_my_display_name" = "Msg containing my display name"; -//"settings_messages_my_user_name" = "Msg containing my user name"; -//"settings_messages_sent_to_me" = "Messages sent to me"; -//"settings_invited_to_room" = "When i'm invited to a room"; -//"settings_join_leave_rooms" = "When people join or leave rooms"; +"settings_on_denied_notification" = "Notifications are denied for %@, please allow them in your device settings"; +"settings_default" = "Default Notifications"; +"settings_meantions_and_keywords" = "Mentions and Keywords"; +"settings_other" = "Other"; //"settings_call_invitations" = "Call invitations"; "settings_enable_callkit" = "Integrated calling"; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 0b77710ce..689355849 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -147,6 +147,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: MajorUpdateViewController.self) } + internal enum NotificationSettingsViewController: StoryboardType { + internal static let storyboardName = "NotificationSettingsViewController" + + internal static let initialScene = InitialSceneType(storyboard: NotificationSettingsViewController.self) + } internal enum QRCodeReaderViewController: StoryboardType { internal static let storyboardName = "QRCodeReaderViewController" diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index e76a83cab..5d9aea944 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -1034,7 +1034,7 @@ title:title handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { - if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + if ([BuildSettings showNotificationsV2]) { [self changeEditedRoomNotificationSettings]; } @@ -1049,7 +1049,7 @@ muteAction.backgroundColor = actionBackgroundColor; UIImage *notificationImage; - if([BuildSettings roomSettingsScreenShowNotificationsV2]) + if([BuildSettings showNotificationsV2]) { notificationImage = isMuted ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; } diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index 74fef7d37..19025bb6c 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -348,7 +348,7 @@ tableViewCell.notificationsButton.tag = room.isMute || room.isMentionsOnly; [tableViewCell.notificationsButton addTarget:self action:@selector(onNotificationsButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + if ([BuildSettings showNotificationsV2]) { tableViewCell.notificationsImageView.image = tableViewCell.notificationsButton.tag ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; } @@ -672,7 +672,7 @@ MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId]; if (room) { - if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + if ([BuildSettings showNotificationsV2]) { [self changeEditedRoomNotificationSettings]; } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift index ddc14d605..daa9b2947 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift @@ -169,7 +169,7 @@ final class RoomInfoListViewController: UIViewController { var rows = [rowSettings] - if BuildSettings.roomSettingsScreenShowNotificationsV2 { + if BuildSettings.showNotificationsV2 { rows.append(roomNotifications) } if RiotSettings.shared.roomInfoScreenShowIntegrations { diff --git a/Riot/Modules/Room/Settings/RoomSettingsViewController.m b/Riot/Modules/Room/Settings/RoomSettingsViewController.m index 44bff4dc2..90adddf3b 100644 --- a/Riot/Modules/Room/Settings/RoomSettingsViewController.m +++ b/Riot/Modules/Room/Settings/RoomSettingsViewController.m @@ -528,7 +528,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT]; } - if (!BuildSettings.roomSettingsScreenShowNotificationsV2) + if (!BuildSettings.showNotificationsV2) { [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS]; } diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsBridgePresenter.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsBridgePresenter.swift index a51e13afe..4fba909c5 100644 --- a/Riot/Modules/Settings/Notifications/NotificationSettingsBridgePresenter.swift +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsBridgePresenter.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2021 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,5 +13,89 @@ // See the License for the specific language governing permissions and // limitations under the License. // - import Foundation + +@objc protocol NotificationSettingsCoordinatorBridgePresenterDelegate { + func notificationSettingsCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: NotificationSettingsCoordinatorBridgePresenter) +} + +/// NotificationSettingsCoordinatorBridgePresenter enables to start NotificationSettingsCoordinator 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 NotificationSettingsCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var coordinator: NotificationSettingsCoordinator? + private var router: NavigationRouter? + + // MARK: Public + + weak var delegate: NotificationSettingsCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + super.init() + } + + // MARK: - Public + + func push(from navigationController: UINavigationController, animated: Bool, popCompletion: (() -> Void)?) { + + let router = NavigationRouter(navigationController: navigationController) + + let notificationSettingsCoordinator = NotificationSettingsCoordinator(session: session) + + router.push(notificationSettingsCoordinator, animated: animated) { [weak self] in + self?.coordinator = nil + self?.router = nil + popCompletion?() + } + + notificationSettingsCoordinator.start() + + self.coordinator = notificationSettingsCoordinator + self.router = router + } + + 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: - NotificationSettingsCoordinatorDelegate +extension NotificationSettingsCoordinatorBridgePresenter: NotificationSettingsCoordinatorDelegate { + func notificationSettingsCoordinator(_ coordinator: NotificationSettingsCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) { + self.delegate?.notificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } + + func notificationSettingsCoordinatorDidCancel(_ coordinator: NotificationSettingsCoordinatorType) { + self.delegate?.notificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } +} + +// MARK: - UIAdaptivePresentationControllerDelegate + +extension NotificationSettingsCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { + + func notificationSettingsCoordinatorDidComplete(_ presentationController: UIPresentationController) { + self.delegate?.notificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } + +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinator.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinator.swift new file mode 100644 index 000000000..c5cce7097 --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinator.swift @@ -0,0 +1,71 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 NotificationSettingsCoordinator: NotificationSettingsCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var notificationSettingsViewModel: NotificationSettingsViewModelType + private let notificationSettingsViewController: NotificationSettingsViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: NotificationSettingsCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + + let notificationSettingsViewModel = NotificationSettingsViewModel(session: self.session) + let notificationSettingsViewController = NotificationSettingsViewController.instantiate(with: notificationSettingsViewModel) + self.notificationSettingsViewModel = notificationSettingsViewModel + self.notificationSettingsViewController = notificationSettingsViewController + } + + // MARK: - Public methods + + func start() { + self.notificationSettingsViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.notificationSettingsViewController + } +} + +// MARK: - NotificationSettingsViewModelCoordinatorDelegate +extension NotificationSettingsCoordinator: NotificationSettingsViewModelCoordinatorDelegate { + + func notificationSettingsViewModel(_ viewModel: NotificationSettingsViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) { + self.delegate?.notificationSettingsCoordinator(self, didCompleteWithUserDisplayName: userDisplayName) + } + + func notificationSettingsViewModelDidCancel(_ viewModel: NotificationSettingsViewModelType) { + self.delegate?.notificationSettingsCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinatorType.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinatorType.swift new file mode 100644 index 000000000..5f588177d --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 NotificationSettingsCoordinatorDelegate: AnyObject { + func notificationSettingsCoordinator(_ coordinator: NotificationSettingsCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) + func notificationSettingsCoordinatorDidCancel(_ coordinator: NotificationSettingsCoordinatorType) +} + +/// `NotificationSettingsCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol NotificationSettingsCoordinatorType: Coordinator, Presentable { + var delegate: NotificationSettingsCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewAction.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsViewAction.swift new file mode 100644 index 000000000..0124bd2a0 --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 + +/// NotificationSettingsViewController view actions exposed to view model +enum NotificationSettingsViewAction { + case loadData + case complete + case cancel +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.storyboard b/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.storyboard new file mode 100644 index 000000000..de82997c2 --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.storyboard @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.swift new file mode 100644 index 000000000..50d01ecb1 --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewController.swift @@ -0,0 +1,178 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 NotificationSettingsViewController: 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: NotificationSettingsViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: NotificationSettingsViewModelType) -> NotificationSettingsViewController { + let viewController = StoryboardScene.NotificationSettingsViewController.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.notificationSettingsTitle" + } + + private func render(viewState: NotificationSettingsViewState) { + 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: - NotificationSettingsViewModelViewDelegate +extension NotificationSettingsViewController: NotificationSettingsViewModelViewDelegate { + + func notificationSettingsViewModel(_ viewModel: NotificationSettingsViewModelType, didUpdateViewState viewSate: NotificationSettingsViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewModel.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsViewModel.swift new file mode 100644 index 000000000..9043b83f9 --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewModel.swift @@ -0,0 +1,91 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 NotificationSettingsViewModel: NotificationSettingsViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + + private var currentOperation: MXHTTPOperation? + private var userDisplayName: String? + + // MARK: Public + + weak var viewDelegate: NotificationSettingsViewModelViewDelegate? + weak var coordinatorDelegate: NotificationSettingsViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + } + + deinit { + self.cancelOperations() + } + + // MARK: - Public + + func process(viewAction: NotificationSettingsViewAction) { + switch viewAction { + case .loadData: + self.loadData() + case .complete: + self.coordinatorDelegate?.notificationSettingsViewModel(self, didCompleteWithUserDisplayName: self.userDisplayName) + case .cancel: + self.cancelOperations() + self.coordinatorDelegate?.notificationSettingsViewModelDidCancel(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: NotificationSettingsViewState) { + self.viewDelegate?.notificationSettingsViewModel(self, didUpdateViewState: viewState) + } + + private func cancelOperations() { + self.currentOperation?.cancel() + } +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewModelType.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsViewModelType.swift new file mode 100644 index 000000000..55088947f --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 NotificationSettingsViewModelViewDelegate: AnyObject { + func notificationSettingsViewModel(_ viewModel: NotificationSettingsViewModelType, didUpdateViewState viewSate: NotificationSettingsViewState) +} + +protocol NotificationSettingsViewModelCoordinatorDelegate: AnyObject { + func notificationSettingsViewModel(_ viewModel: NotificationSettingsViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) + func notificationSettingsViewModelDidCancel(_ viewModel: NotificationSettingsViewModelType) +} + +/// Protocol describing the view model used by `NotificationSettingsViewController` +protocol NotificationSettingsViewModelType { + + var viewDelegate: NotificationSettingsViewModelViewDelegate? { get set } + var coordinatorDelegate: NotificationSettingsViewModelCoordinatorDelegate? { get set } + + func process(viewAction: NotificationSettingsViewAction) +} diff --git a/Riot/Modules/Settings/Notifications/NotificationSettingsViewState.swift b/Riot/Modules/Settings/Notifications/NotificationSettingsViewState.swift new file mode 100644 index 000000000..f14485d4e --- /dev/null +++ b/Riot/Modules/Settings/Notifications/NotificationSettingsViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Settings/Notifications NotificationSettings +/* + 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 + +/// NotificationSettingsViewController view state +enum NotificationSettingsViewState { + case loading + case loaded(_ displayName: String) + case error(Error) +} diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 29765dd4a..408e70a74 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -94,6 +94,9 @@ enum NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX, NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX, NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX, + NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX, + NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX, + NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX, }; enum @@ -156,6 +159,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void); #pragma mark - SettingsViewController @interface SettingsViewController () @property (nonatomic) UNNotificationSettings *systemNotificationSettings; @property (nonatomic, weak) DeactivateAccountViewController *deactivateAccountViewController; +@property (nonatomic, strong) NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter; @property (nonatomic, strong) SignOutAlertPresenter *signOutAlertPresenter; @property (nonatomic, weak) UIButton *signOutButton; @property (nonatomic, strong) SingleImagePickerPresenter *imagePickerPresenter; @@ -361,10 +366,18 @@ TableViewSectionsDelegate> { [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT]; } - [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX]; + if (![BuildSettings showNotificationsV2]){ + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX]; + } + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX]; - sectionNotificationSettings.headerTitle = NSLocalizedStringFromTable(@"settings_notifications_settings", @"Vector", nil); + if ([BuildSettings showNotificationsV2]){ + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX]; + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX]; + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX]; + } + sectionNotificationSettings.headerTitle = NSLocalizedStringFromTable(@"settings_notifications", @"Vector", nil); [tmpSections addObject:sectionNotificationSettings]; if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString) @@ -1896,6 +1909,23 @@ TableViewSectionsDelegate> cell = labelAndSwitchCell; } + else if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX) + { + cell = [self getDefaultTableViewCell:tableView]; + if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX) + { + cell.textLabel.text = NSLocalizedStringFromTable(@"settings_default", @"Vector", nil); + } + else if (row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX) + { + cell.textLabel.text = NSLocalizedStringFromTable(@"settings_meantions_and_keywords", @"Vector", nil); + } + else if (row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX) + { + cell.textLabel.text = NSLocalizedStringFromTable(@"settings_other", @"Vector", nil); + } + [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; + } } else if (section == SECTION_TAG_CALLS) { @@ -2712,6 +2742,10 @@ TableViewSectionsDelegate> } } } + else if (section == SECTION_TAG_NOTIFICATIONS) + { + [self showNotificationSettings]; + } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @@ -4046,6 +4080,35 @@ TableViewSectionsDelegate> [deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil]; } +#pragma mark - NotificationSettingsCoordinatorBridgePresenter + +- (void)showNotificationSettings +{ + + NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter = [[NotificationSettingsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + notificationSettingsBridgePresenter.delegate = self; + + MXWeakify(self); + + [notificationSettingsBridgePresenter pushFrom:self.navigationController animated:YES popCompletion:^{ + MXStrongifyAndReturnIfNil(self); + + self.notificationSettingsBridgePresenter = nil; + }]; + + + self.notificationSettingsBridgePresenter = notificationSettingsBridgePresenter; +} + +#pragma mark - NotificationSettingsCoordinatorBridgePresenterDelegate + +- (void)notificationSettingsCoordinatorBridgePresenterDelegateDidComplete:(NotificationSettingsCoordinatorBridgePresenter *)coordinatorBridgePresenter +{ + [self.notificationSettingsBridgePresenter dismissWithAnimated:YES completion:nil]; + self.notificationSettingsBridgePresenter = nil; +} + + #pragma mark - SecureBackupSetupCoordinatorBridgePresenter - (void)showSecureBackupSetupFromSignOutFlow