diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 02f47cf22..c6a6f7ca9 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -420,4 +420,8 @@ final class BuildSettings: NSObject { // MARK: - New App Layout static let newAppLayoutEnabled = false + + // MARK: - Device manager + + static let deviceManagerEnabled = false } diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 618f8f5e5..17faac6e0 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -894,6 +894,9 @@ Tap the + to start adding people."; "manage_session_not_trusted" = "Not trusted"; "manage_session_sign_out" = "Sign out of this session"; +// User sessions management +"user_sessions_settings" = "Manage sessions"; + // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "This app does not support the authentication mechanism on your homeserver."; @@ -2332,6 +2335,10 @@ To enable access, tap Settings> Location and select Always"; "location_sharing_live_lab_promotion_text" = "Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room."; "location_sharing_live_lab_promotion_activation" = "Enable live location sharing"; +// MARK: User sessions management + +"user_sessions_overview_title" = "Sessions"; + // MARK: - MatrixKit diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 5ef730dfc..2ba71d637 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -8391,6 +8391,14 @@ public class VectorL10n: NSObject { public static var userIdTitle: String { return VectorL10n.tr("Vector", "user_id_title") } + /// Sessions + public static var userSessionsOverviewTitle: String { + return VectorL10n.tr("Vector", "user_sessions_overview_title") + } + /// Manage sessions + public static var userSessionsSettings: String { + return VectorL10n.tr("Vector", "user_sessions_settings") + } /// If you didn’t sign in to this session, your account may be compromised. public static var userVerificationSessionDetailsAdditionalInformationUntrustedCurrentUser: String { return VectorL10n.tr("Vector", "user_verification_session_details_additional_information_untrusted_current_user") diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index fd6df1095..6b9a19f97 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -178,6 +178,7 @@ typedef NS_ENUM(NSUInteger, LABS_ENABLE) typedef NS_ENUM(NSUInteger, SECURITY) { SECURITY_BUTTON_INDEX = 0, + DEVICE_MANAGER_INDEX }; typedef void (^blockSettingsViewController_onReadyToDestroy)(void); @@ -284,6 +285,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate> @property (nonatomic, strong) ThreadsBetaCoordinatorBridgePresenter *threadsBetaBridgePresenter; @property (nonatomic, strong) ChangePasswordCoordinatorBridgePresenter *changePasswordBridgePresenter; +@property (nonatomic, strong) UserSessionsFlowCoordinatorBridgePresenter *userSessionsFlowCoordinatorBridgePresenter; /** Whether or not to check for contacts access after the user accepts the service terms. The value of this property is @@ -400,6 +402,13 @@ ChangePasswordCoordinatorBridgePresenterDelegate> Section *sectionSecurity = [Section sectionWithTag:SECTION_TAG_SECURITY]; [sectionSecurity addRowWithTag:SECURITY_BUTTON_INDEX]; + + if (BuildSettings.deviceManagerEnabled) + { + // NOTE: Add device manager entry point in the security section atm for debug purpose + [sectionSecurity addRowWithTag:DEVICE_MANAGER_INDEX]; + } + sectionSecurity.headerTitle = [VectorL10n settingsSecurity]; [tmpSections addObject:sectionSecurity]; @@ -2533,6 +2542,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> cell.textLabel.text = [VectorL10n securitySettingsTitle]; [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; break; + case DEVICE_MANAGER_INDEX: + cell = [self getDefaultTableViewCell:tableView]; + cell.textLabel.text = [VectorL10n userSessionsSettings]; + [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; + break; } } else if (section == SECTION_TAG_DEACTIVATE_ACCOUNT) @@ -2885,6 +2899,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [self pushViewController:securityViewController]; break; } + case DEVICE_MANAGER_INDEX: + { + [self showUserSessionsFlow]; + break; + } } } else if (section == SECTION_TAG_NOTIFICATIONS) @@ -4523,4 +4542,35 @@ ChangePasswordCoordinatorBridgePresenterDelegate> self.changePasswordBridgePresenter = nil; } +#pragma mark - User sessions management + +- (void)showUserSessionsFlow +{ + if (!self.mainSession) + { + MXLogError(@"[SettingsViewController] Cannot show user sessions flow, no user session available"); + return; + } + + if (!self.navigationController) + { + MXLogError(@"[SettingsViewController] Cannot show user sessions flow, no navigation controller available"); + return; + } + + UserSessionsFlowCoordinatorBridgePresenter *userSessionsFlowCoordinatorBridgePresenter = [[UserSessionsFlowCoordinatorBridgePresenter alloc] initWithMxSession:self.mainSession]; + + MXWeakify(self); + + userSessionsFlowCoordinatorBridgePresenter.completion = ^{ + MXStrongifyAndReturnIfNil(self); + + self.userSessionsFlowCoordinatorBridgePresenter = nil; + }; + + self.userSessionsFlowCoordinatorBridgePresenter = userSessionsFlowCoordinatorBridgePresenter; + + [self.userSessionsFlowCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES]; +} + @end diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinator.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinator.swift new file mode 100644 index 000000000..42151e7cf --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinator.swift @@ -0,0 +1,76 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import CommonKit + +struct UserSessionsFlowCoordinatorParameters { + let session: MXSession + let router: NavigationRouterType? +} + +final class UserSessionsFlowCoordinator: Coordinator, Presentable { + + // MARK: - Properties + + // MARK: Private + + private let parameters: UserSessionsFlowCoordinatorParameters + private let navigationRouter: NavigationRouterType + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var completion: (() -> Void)? + + // MARK: - Setup + + init(parameters: UserSessionsFlowCoordinatorParameters) { + self.parameters = parameters + + self.navigationRouter = parameters.router ?? NavigationRouter(navigationController: RiotNavigationController()) + } + + // MARK: - Public + + func start() { + MXLog.debug("[UserSessionsFlowCoordinator] did start.") + + let rootCoordinatorParameters = UserSessionsOverviewCoordinatorParameters(session: self.parameters.session) + + let rootCoordinator = UserSessionsOverviewCoordinator(parameters: rootCoordinatorParameters) + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + if self.navigationRouter.modules.isEmpty == false { + self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in + self?.remove(childCoordinator: rootCoordinator) + self?.completion?() + }) + } else { + self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in + self?.remove(childCoordinator: rootCoordinator) + self?.completion?() + } + } + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinatorBridgePresenter.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..d2a1a9ff0 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsFlow/Coordinator/UserSessionsFlowCoordinatorBridgePresenter.swift @@ -0,0 +1,83 @@ +/* + Copyright 2022 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +import MatrixSDK +import UIKit + +/// UserSessionsFlowCoordinatorBridgePresenter enables to start UserSessionsFlowCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). +/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +@objcMembers +final class UserSessionsFlowCoordinatorBridgePresenter: NSObject { + + // MARK: - Constants + + // MARK: - Properties + + // MARK: Private + + private let mxSession: MXSession + private var coordinator: UserSessionsFlowCoordinator? + + // MARK: Public + + var completion: (() -> Void)? + + // MARK: - Setup + + init(mxSession: MXSession) { + self.mxSession = mxSession + super.init() + } + + // MARK: - Public + + func push(from navigationController: UINavigationController, animated: Bool) { + + self.startUserSessionsFlow(mxSession: self.mxSession, navigationController: navigationController) + } + + // MARK: - Private + + private func startUserSessionsFlow(mxSession: MXSession, navigationController: UINavigationController?) { + + var navigationRouter: NavigationRouterType? + + if let navigationController = navigationController { + navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController) + } + + let coordinatorParameters = UserSessionsFlowCoordinatorParameters(session: mxSession, router: navigationRouter) + + let userSessionsFlowCoordinator = UserSessionsFlowCoordinator(parameters: coordinatorParameters) + + userSessionsFlowCoordinator.completion = { [weak self] in + + guard let self = self else { + return + } + + self.completion?() + self.coordinator = nil + } + + userSessionsFlowCoordinator.start() + + self.coordinator = userSessionsFlowCoordinator + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Coordinator/UserSessionsOverviewCoordinator.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Coordinator/UserSessionsOverviewCoordinator.swift new file mode 100644 index 000000000..5abebc3ab --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Coordinator/UserSessionsOverviewCoordinator.swift @@ -0,0 +1,67 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import CommonKit + +struct UserSessionsOverviewCoordinatorParameters { + let session: MXSession +} + +final class UserSessionsOverviewCoordinator: Coordinator, Presentable { + + // MARK: - Properties + + // MARK: Private + + private let parameters: UserSessionsOverviewCoordinatorParameters + private let userSessionsOverviewHostingController: UIViewController + private var userSessionsOverviewViewModel: UserSessionsOverviewViewModelProtocol + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var completion: (() -> Void)? + + // MARK: - Setup + + init(parameters: UserSessionsOverviewCoordinatorParameters) { + self.parameters = parameters + let viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: UserSessionsOverviewService()) + let view = UserSessionsOverview(viewModel: viewModel.context) + userSessionsOverviewViewModel = viewModel + userSessionsOverviewHostingController = VectorHostingController(rootView: view) + } + + // MARK: - Public + + func start() { + MXLog.debug("[UserSessionsOverviewCoordinator] did start.") + userSessionsOverviewViewModel.completion = { [weak self] result in + guard let self = self else { return } + MXLog.debug("[UserSessionsOverviewCoordinator] UserSessionsOverviewViewModel did complete with result: \(result).") + switch result { + case .done: + self.completion?() + } + } + } + + func toPresentable() -> UIViewController { + return self.userSessionsOverviewHostingController + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/MockUserSessionsOverviewScreenState.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/MockUserSessionsOverviewScreenState.swift new file mode 100644 index 000000000..a0b67dafc --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/MockUserSessionsOverviewScreenState.swift @@ -0,0 +1,57 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import SwiftUI + +/// Using an enum for the screen allows you define the different state cases with +/// the relevant associated data for each case. +enum MockUserSessionsOverviewScreenState: MockScreenState, CaseIterable { + // A case for each state you want to represent + // with specific, minimal associated data that will allow you + // mock that screen. + case verifiedSession + + /// The associated screen + var screenType: Any.Type { + UserSessionsOverview.self + } + + /// A list of screen state definitions + static var allCases: [MockUserSessionsOverviewScreenState] { + // Each of the presence statuses + return [.verifiedSession] + } + + /// Generate the view struct for the screen state. + var screenView: ([Any], AnyView) { + let service: MockUserSessionsOverviewService = MockUserSessionsOverviewService() + switch self { + case .verifiedSession: + break + } + + let viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: service) + + // can simulate service and viewModel actions here if needs be. + + return ( + [service, viewModel], + AnyView(UserSessionsOverview(viewModel: viewModel.context) + .addDependency(MockAvatarService.example)) + ) + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift new file mode 100644 index 000000000..1f280ad1a --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift @@ -0,0 +1,25 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol { + + // MARK: - Setup + + init() { + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/Mock/MockUserSessionsOverviewService.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/Mock/MockUserSessionsOverviewService.swift new file mode 100644 index 000000000..61037fec8 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/Mock/MockUserSessionsOverviewService.swift @@ -0,0 +1,20 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol { +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/UserSessionsOverviewServiceProtocol.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/UserSessionsOverviewServiceProtocol.swift new file mode 100644 index 000000000..be124d126 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/UserSessionsOverviewServiceProtocol.swift @@ -0,0 +1,20 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +protocol UserSessionsOverviewServiceProtocol { +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/UI/UserSessionsOverviewUITests.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/UI/UserSessionsOverviewUITests.swift new file mode 100644 index 000000000..aa0cf5e87 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/UI/UserSessionsOverviewUITests.swift @@ -0,0 +1,21 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import RiotSwiftUI + +class UserSessionsOverviewUITests: MockScreenTestCase { +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/Unit/UserSessionsOverviewViewModelTests.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/Unit/UserSessionsOverviewViewModelTests.swift new file mode 100644 index 000000000..d45804590 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Test/Unit/UserSessionsOverviewViewModelTests.swift @@ -0,0 +1,33 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import Combine + +@testable import RiotSwiftUI + +class UserSessionsOverviewViewModelTests: XCTestCase { + + var service: MockUserSessionsOverviewService! + var viewModel: UserSessionsOverviewViewModelProtocol! + var context: UserSessionsOverviewViewModelType.Context! + + override func setUpWithError() throws { + service = MockUserSessionsOverviewService() + viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: service) + context = viewModel.context + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewModels.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewModels.swift new file mode 100644 index 000000000..949617ca6 --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewModels.swift @@ -0,0 +1,38 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: - Coordinator + +// MARK: View model + +enum UserSessionsOverviewViewModelResult { + case done +} + +// MARK: View + +struct UserSessionsOverviewViewState: BindableState { +} + +enum UserSessionsOverviewViewAction { + case verifyCurrentSession + case viewCurrentSessionDetails + case viewAllUnverifiedSessions + case viewAllInactiveSessions + case viewAllOtherSessions +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModel.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModel.swift new file mode 100644 index 000000000..93c28688c --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModel.swift @@ -0,0 +1,61 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +typealias UserSessionsOverviewViewModelType = StateStoreViewModel + +class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSessionsOverviewViewModelProtocol { + + // MARK: - Properties + + // MARK: Private + + private let userSessionsOverviewService: UserSessionsOverviewServiceProtocol + + // MARK: Public + + var completion: ((UserSessionsOverviewViewModelResult) -> Void)? + + // MARK: - Setup + + init(userSessionsOverviewService: UserSessionsOverviewServiceProtocol) { + self.userSessionsOverviewService = userSessionsOverviewService + + let viewState = UserSessionsOverviewViewState() + + super.init(initialViewState: viewState) + } + + // MARK: - Public + + override func process(viewAction: UserSessionsOverviewViewAction) { + switch viewAction { + case .verifyCurrentSession: + break + case .viewCurrentSessionDetails: + break + case .viewAllUnverifiedSessions: + break + case .viewAllInactiveSessions: + break + case .viewAllOtherSessions: + break + } + } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModelProtocol.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModelProtocol.swift new file mode 100644 index 000000000..f416bb16a --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewViewModelProtocol.swift @@ -0,0 +1,24 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +protocol UserSessionsOverviewViewModelProtocol { + + var completion: ((UserSessionsOverviewViewModelResult) -> Void)? { get set } + + var context: UserSessionsOverviewViewModelType.Context { get } +} diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift new file mode 100644 index 000000000..1dc2983fb --- /dev/null +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -0,0 +1,47 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct UserSessionsOverview: View { + + // MARK: - Properties + + // MARK: Private + + @Environment(\.theme) private var theme: ThemeSwiftUI + + // MARK: Public + + @ObservedObject var viewModel: UserSessionsOverviewViewModel.Context + + var body: some View { + VStack { + } + .background(theme.colors.background) + .frame(maxHeight: .infinity) + .navigationTitle(VectorL10n.userSessionsOverviewTitle) + } +} + +// MARK: - Previews + +struct UserSessionsOverview_Previews: PreviewProvider { + static let stateRenderer = MockUserSessionsOverviewScreenState.stateRenderer + static var previews: some View { + stateRenderer.screenGroup() + } +} diff --git a/changelog.d/6585.wip b/changelog.d/6585.wip new file mode 100644 index 000000000..d02a0134e --- /dev/null +++ b/changelog.d/6585.wip @@ -0,0 +1 @@ +Device manager: Add new session management screen.