Skip to content

Commit

Permalink
QR login from device manager (#6818)
Browse files Browse the repository at this point in the history
* Add link device button into the sessions overview screen

* Run Swift format

* Fix tests

* Fix a crash in tests

* Fix PR remark
  • Loading branch information
ismailgulek authored Oct 7, 2022
1 parent 561f4d8 commit 1ee8c9c
Show file tree
Hide file tree
Showing 33 changed files with 223 additions and 126 deletions.
2 changes: 1 addition & 1 deletion Config/CommonConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class CommonConfiguration: NSObject, Configurable {

func setupSettingsWhenLoaded(for matrixSession: MXSession) {
// Do not warn for unknown devices. We have cross-signing now
matrixSession.crypto.warnOnUnknowDevices = false
matrixSession.crypto?.warnOnUnknowDevices = false
}

}
1 change: 1 addition & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2407,6 +2407,7 @@ To enable access, tap Settings> Location and select Always";
"user_sessions_overview_other_sessions_section_info" = "For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore.";

"user_sessions_overview_current_session_section_title" = "Current session";
"user_sessions_overview_link_device" = "Link a device";

"user_sessions_view_all_action" = "View all (%d)";

Expand Down
4 changes: 4 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8747,6 +8747,10 @@ public class VectorL10n: NSObject {
public static var userSessionsOverviewCurrentSessionSectionTitle: String {
return VectorL10n.tr("Vector", "user_sessions_overview_current_session_section_title")
}
/// Link a device
public static var userSessionsOverviewLinkDevice: String {
return VectorL10n.tr("Vector", "user_sessions_overview_link_device")
}
/// For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore.
public static var userSessionsOverviewOtherSessionsSectionInfo: String {
return VectorL10n.tr("Vector", "user_sessions_overview_other_sessions_section_info")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,21 @@ struct UserSessionCardViewPreview: View {

init(isCurrent: Bool = false) {
let sessionInfo = UserSessionInfo(id: "alice",
name: "iOS",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "iOS 15.5",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: isCurrent)
name: "iOS",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "iOS 15.5",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: isCurrent)
viewData = UserSessionCardViewData(sessionInfo: sessionInfo)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
init(parameters: UserSessionsFlowCoordinatorParameters) {
self.parameters = parameters

self.navigationRouter = parameters.router
navigationRouter = parameters.router
errorPresenter = MXKErrorAlertPresentation()
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: parameters.router.toPresentable())
}
Expand Down Expand Up @@ -75,6 +75,8 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
self.openOtherSessions(sessionInfos: sessionInfos,
filterBy: filter,
title: VectorL10n.userOtherSessionSecurityRecommendationTitle)
case .linkDevice:
self.openQRLoginScreen()
}
}
return coordinator
Expand Down Expand Up @@ -105,6 +107,21 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
}
pushScreen(with: coordinator)
}

/// Shows the QR login screen.
private func openQRLoginScreen() {
let service = QRLoginService(client: parameters.session.matrixRestClient,
mode: .authenticated)
let parameters = AuthenticationQRLoginStartCoordinatorParameters(navigationRouter: navigationRouter,
qrLoginService: service)
let coordinator = AuthenticationQRLoginStartCoordinator(parameters: parameters)
coordinator.callback = { [weak self, weak coordinator] _ in
guard let self = self, let coordinator = coordinator else { return }
self.remove(childCoordinator: coordinator)
}

pushScreen(with: coordinator)
}

private func createUserSessionOverviewCoordinator(sessionInfo: UserSessionInfo) -> UserSessionOverviewCoordinator {
let parameters = UserSessionOverviewCoordinatorParameters(session: parameters.session,
Expand Down Expand Up @@ -135,7 +152,6 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
return UserOtherSessionsCoordinator(parameters: parameters)
}


/// Shows a confirmation dialog to the user to sign out of a session.
private func showLogoutConfirmation(for sessionInfo: UserSessionInfo) {
// Use a UIAlertController as we don't have confirmationDialog in SwiftUI on iOS 14.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ struct UserOtherSessionsCoordinatorParameters {
}

final class UserOtherSessionsCoordinator: Coordinator, Presentable {

private let parameters: UserOtherSessionsCoordinatorParameters
private let userOtherSessionsHostingController: UIViewController
private var userOtherSessionsViewModel: UserOtherSessionsViewModelProtocol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {

/// Generate the view struct for the screen state.
var screenView: ([Any], AnyView) {

let viewModel: UserOtherSessionsViewModel
switch self {
case .inactiveSessions:
Expand Down Expand Up @@ -83,7 +82,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
deviceType: .desktop,
isVerified: true,
lastSeenIP: "1.0.0.1",
lastSeenTimestamp: Date().timeIntervalSince1970 - 8000000,
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
Expand All @@ -99,7 +98,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
deviceType: .web,
isVerified: true,
lastSeenIP: "2.0.0.2",
lastSeenTimestamp: Date().timeIntervalSince1970 - 9000000,
lastSeenTimestamp: Date().timeIntervalSince1970 - 9_000_000,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
Expand All @@ -115,7 +114,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
deviceType: .mobile,
isVerified: false,
lastSeenIP: "3.0.0.3",
lastSeenTimestamp: Date().timeIntervalSince1970 - 10000000,
lastSeenTimestamp: Date().timeIntervalSince1970 - 10_000_000,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
Expand Down Expand Up @@ -150,7 +149,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
deviceType: .desktop,
isVerified: false,
lastSeenIP: "1.0.0.1",
lastSeenTimestamp: Date().timeIntervalSince1970 - 8000000,
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
Expand All @@ -160,7 +159,6 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
clientName: nil,
clientVersion: nil,
isActive: true,
isCurrent: false)
]
isCurrent: false)]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ import RiotSwiftUI
import XCTest

class UserOtherSessionsUITests: MockScreenTestCase {

func test_whenOtherSessionsWithInactiveSessionFilterPresented_correctHeaderDisplayed() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.inactiveSessions.title)

XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo].exists)
}

func test_whenOtherSessionsWithInactiveSessionFilterPresented_correctItemsDisplayed() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ import XCTest
@testable import RiotSwiftUI

class UserOtherSessionsViewModelTests: XCTestCase {


func test_whenUserOtherSessionSelectedProcessed_completionWithShowUserSessionOverviewCalled() {
let expectedUserSessionInfo = createUserSessionInfo(sessionId: "session 2")
let sut = UserOtherSessionsViewModel(sessionInfos: [createUserSessionInfo(sessionId: "session 1"),
expectedUserSessionInfo],
filter: .inactive,
title: "Title")
expectedUserSessionInfo],
filter: .inactive,
title: "Title")

var modelResult: UserOtherSessionsViewModelResult?
sut.completion = { result in
Expand All @@ -39,8 +37,8 @@ class UserOtherSessionsViewModelTests: XCTestCase {
func test_whenModelCreated_withInactiveFilter_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"), createUserSessionInfo(sessionId: "session 2")]
let sut = UserOtherSessionsViewModel(sessionInfos: sessionInfos,
filter: .inactive,
title: "Title")
filter: .inactive,
title: "Title")

let expectedHeader = UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle,
subtitle: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo,
Expand All @@ -51,7 +49,6 @@ class UserOtherSessionsViewModelTests: XCTestCase {
XCTAssertEqual(sut.state, expectedState)
}


private func createUserSessionInfo(sessionId: String) -> UserSessionInfo {
UserSessionInfo(id: sessionId,
name: "iOS",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import Foundation

// MARK: - Coordinator

enum UserOtherSessionsCoordinatorResult {
case openSessionDetails(sessionInfo: UserSessionInfo)
}
Expand All @@ -38,6 +39,7 @@ enum UserOtherSessionsSection: Hashable, Identifiable {
var id: Self {
self
}

case sessionItems(header: UserOtherSessionsHeaderViewData, items: [UserSessionListItemViewData])
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ enum OtherUserSessionsFilter {
}

class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessionsViewModelProtocol {

var completion: ((UserOtherSessionsViewModelResult) -> Void)?
private let sessionInfos: [UserSessionInfo]

Expand All @@ -42,7 +41,7 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
override func process(viewAction: UserOtherSessionsViewAction) {
switch viewAction {
case let .userOtherSessionSelected(sessionId: sessionId):
guard let session = sessionInfos.first(where: {$0.id == sessionId}) else {
guard let session = sessionInfos.first(where: { $0.id == sessionId }) else {
assertionFailure("Session should exist in the array.")
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import SwiftUI

struct UserOtherSessions: View {

@Environment(\.theme) private var theme

@ObservedObject var viewModel: UserOtherSessionsViewModel.Context
Expand Down Expand Up @@ -57,7 +56,6 @@ struct UserOtherSessions: View {
// MARK: - Previews

struct UserOtherSessions_Previews: PreviewProvider {

static let stateRenderer = MockUserOtherSessionsScreenState.stateRenderer

static var previews: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ struct UserOtherSessionsHeaderViewData: Hashable {
}

struct UserOtherSessionsHeaderView: View {

private var backgroundShape: RoundedRectangle {
RoundedRectangle(cornerRadius: 8)
}
Expand All @@ -33,7 +32,7 @@ struct UserOtherSessionsHeaderView: View {
let viewData: UserOtherSessionsHeaderViewData

var body: some View {
HStack (alignment: .top, spacing: 0) {
HStack(alignment: .top, spacing: 0) {
if let iconName = viewData.iconName {
Image(iconName)
.frame(width: 40, height: 40)
Expand Down Expand Up @@ -63,12 +62,10 @@ struct UserOtherSessionsHeaderView: View {
// MARK: - Previews

struct UserOtherSessionsHeaderView_Previews: PreviewProvider {

private static let inactiveSessionViewData = UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle,
subtitle: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo,
iconName: Asset.Images.userOtherSessionsInactive.name)


static var previews: some View {
Group {
UserOtherSessionsHeaderView(viewData: self.inactiveSessionViewData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,38 +42,38 @@ enum MockUserSessionDetailsScreenState: MockScreenState, CaseIterable {
switch self {
case .allSections:
sessionInfo = UserSessionInfo(id: "alice",
name: "iOS",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "iOS 15.5",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: true)
name: "iOS",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "iOS 15.5",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: true)
case .sessionSectionOnly:
sessionInfo = UserSessionInfo(id: "3",
name: "Android",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "3.0.0.3",
lastSeenTimestamp: Date().timeIntervalSince1970 - 10,
applicationName: "Element Android",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "Android 4.0",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: false)
name: "Android",
deviceType: .mobile,
isVerified: false,
lastSeenIP: "3.0.0.3",
lastSeenTimestamp: Date().timeIntervalSince1970 - 10,
applicationName: "Element Android",
applicationVersion: "1.0.0",
applicationURL: nil,
deviceModel: nil,
deviceOS: "Android 4.0",
lastSeenIPLocation: nil,
clientName: "Element",
clientVersion: "1.0.0",
isActive: true,
isCurrent: false)
}
let viewModel = UserSessionDetailsViewModel(sessionInfo: sessionInfo)

Expand Down
Loading

0 comments on commit 1ee8c9c

Please sign in to comment.