diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index fa68764c3e..c06e9ca295 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2444,6 +2444,7 @@ To enable access, tap Settings> Location and select Always"; "user_session_unverified_additional_info" = "Verify your current session for enhanced secure messaging."; "user_session_verification_unknown_additional_info" = "Verify your current session to reveal this session's verification status."; "user_other_session_unverified_additional_info" = "Verify or sign out from this session for best security and reliability."; +"user_other_session_permanently_unverified_additional_info" = "This session cannot be verified because it does not support encryption."; "user_other_session_verified_additional_info" = "This session is ready for secure messaging."; "user_session_push_notifications" = "Push notifications"; "user_session_push_notifications_message" = "When turned on, this session will receive push notifications."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 36091cc12f..b9d30fc5c8 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -8699,6 +8699,10 @@ public class VectorL10n: NSObject { public static var userOtherSessionNoVerifiedSessions: String { return VectorL10n.tr("Vector", "user_other_session_no_verified_sessions") } + /// This session cannot be verified because it does not support encryption. + public static var userOtherSessionPermanentlyUnverifiedAdditionalInfo: String { + return VectorL10n.tr("Vector", "user_other_session_permanently_unverified_additional_info") + } /// Security recommendation public static var userOtherSessionSecurityRecommendationTitle: String { return VectorL10n.tr("Vector", "user_other_session_security_recommendation_title") diff --git a/RiotSwiftUI/Modules/UserSessions/Common/UserSessionInfo.swift b/RiotSwiftUI/Modules/UserSessions/Common/UserSessionInfo.swift index d3e7690eaa..bee2862946 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/UserSessionInfo.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/UserSessionInfo.swift @@ -79,6 +79,12 @@ struct UserSessionInfo: Identifiable { case unverified /// The session has been verified. case verified + /// A session which cannot be never verified due to lack of crypto support + case permanentlyUnverified + + var isUnverified: Bool { + self == .unverified || self == .permanentlyUnverified + } } } diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarViewData.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarViewData.swift index 1fcf65cf13..ce820eedcc 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarViewData.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarViewData.swift @@ -28,7 +28,7 @@ struct DeviceAvatarViewData: Hashable { switch verificationState { case .verified: return Asset.Images.userSessionVerified.name - case .unverified: + case .unverified, .permanentlyUnverified: return Asset.Images.userSessionUnverified.name case .unknown: return Asset.Images.userSessionVerificationUnknown.name diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift index 60adcb4c8a..d997567f3b 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift @@ -47,7 +47,7 @@ struct UserSessionCardViewData { switch verificationState { case .verified: return Asset.Images.userSessionVerified.name - case .unverified: + case .unverified, .permanentlyUnverified: return Asset.Images.userSessionUnverified.name case .unknown: return Asset.Images.userSessionVerificationUnknown.name @@ -59,7 +59,7 @@ struct UserSessionCardViewData { switch verificationState { case .verified: return VectorL10n.userSessionVerified - case .unverified: + case .unverified, .permanentlyUnverified: return VectorL10n.userSessionUnverified case .unknown: return VectorL10n.userSessionVerificationUnknown @@ -71,7 +71,7 @@ struct UserSessionCardViewData { switch verificationState { case .verified: return \.accent - case .unverified: + case .unverified, .permanentlyUnverified: return \.alert case .unknown: return \.secondaryContent @@ -85,6 +85,8 @@ struct UserSessionCardViewData { return isCurrentSessionDisplayMode ? VectorL10n.userSessionVerifiedAdditionalInfo : VectorL10n.userOtherSessionVerifiedAdditionalInfo + " %@" case .unverified: return isCurrentSessionDisplayMode ? VectorL10n.userSessionUnverifiedAdditionalInfo : VectorL10n.userOtherSessionUnverifiedAdditionalInfo + " %@" + case .permanentlyUnverified: + return VectorL10n.userOtherSessionPermanentlyUnverifiedAdditionalInfo case .unknown: return VectorL10n.userSessionVerificationUnknownAdditionalInfo } diff --git a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift index 78df04c0b5..76c1be95b3 100644 --- a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift +++ b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift @@ -465,7 +465,7 @@ private extension InfoSheetCoordinatorParameters { private extension UserSessionInfo { var bottomSheetTitle: String { switch verificationState { - case .unverified: + case .unverified, .permanentlyUnverified: return VectorL10n.userSessionUnverifiedSessionTitle case .verified: return VectorL10n.userSessionVerifiedSessionTitle @@ -476,7 +476,7 @@ private extension UserSessionInfo { var bottomSheetDescription: String { switch verificationState { - case .unverified: + case .unverified, .permanentlyUnverified: return VectorL10n.userSessionUnverifiedSessionDescription case .verified: return VectorL10n.userSessionVerifiedSessionDescription diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/Unit/UserOtherSessionsViewModelTests.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/Unit/UserOtherSessionsViewModelTests.swift index bce987575c..270891d912 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/Unit/UserOtherSessionsViewModelTests.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/Unit/UserOtherSessionsViewModelTests.swift @@ -87,10 +87,15 @@ class UserOtherSessionsViewModelTests: XCTestCase { func test_whenModelCreated_withUnverifiedFilter_viewStateIsCorrect() { let sessionInfos = [createUserSessionInfo(sessionId: "session 1"), - createUserSessionInfo(sessionId: "session 2")] + createUserSessionInfo(sessionId: "session 2", verificationState: .permanentlyUnverified), + createUserSessionInfo(sessionId: "session 3", verificationState: .unknown)] let sut = createSUT(sessionInfos: sessionInfos, filter: .unverified) - let expectedItems = sessionInfos.filter { !$0.isCurrent }.asViewData() + let expectedItems = sessionInfos + .filter { + !$0.isCurrent && $0.verificationState.isUnverified + } + .asViewData() let bindings = UserOtherSessionsBindings(filter: .unverified, isEditModeEnabled: false) let expectedState = UserOtherSessionsViewState(bindings: bindings, title: "Title", @@ -100,12 +105,13 @@ class UserOtherSessionsViewModelTests: XCTestCase { allItemsSelected: false, enableSignOutButton: false, showLocationInfo: false) + XCTAssertEqual(expectedItems.count, 2) XCTAssertEqual(sut.state, expectedState) } func test_whenModelCreated_withVerifiedFilter_viewStateIsCorrect() { - let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: true), - createUserSessionInfo(sessionId: "session 2", isVerified: true)] + let sessionInfos = [createUserSessionInfo(sessionId: "session 1", verificationState: .verified), + createUserSessionInfo(sessionId: "session 2", verificationState: .verified)] let sut = createSUT(sessionInfos: sessionInfos, filter: .verified) let expectedItems = sessionInfos.filter { !$0.isCurrent }.asViewData() @@ -122,8 +128,8 @@ class UserOtherSessionsViewModelTests: XCTestCase { } func test_whenModelCreated_withVerifiedFilterWithNoVerifiedSessions_viewStateIsCorrect() { - let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: false), - createUserSessionInfo(sessionId: "session 2", isVerified: false)] + let sessionInfos = [createUserSessionInfo(sessionId: "session 1"), + createUserSessionInfo(sessionId: "session 2")] let sut = createSUT(sessionInfos: sessionInfos, filter: .verified) let bindings = UserOtherSessionsBindings(filter: .verified, isEditModeEnabled: false) let expectedState = UserOtherSessionsViewState(bindings: bindings, @@ -138,8 +144,8 @@ class UserOtherSessionsViewModelTests: XCTestCase { } func test_whenModelCreated_withUnverifiedFilterWithNoUnverifiedSessions_viewStateIsCorrect() { - let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: true), - createUserSessionInfo(sessionId: "session 2", isVerified: true)] + let sessionInfos = [createUserSessionInfo(sessionId: "session 1", verificationState: .verified), + createUserSessionInfo(sessionId: "session 2", verificationState: .verified)] let sut = createSUT(sessionInfos: sessionInfos, filter: .unverified) let bindings = UserOtherSessionsBindings(filter: .unverified, isEditModeEnabled: false) let expectedState = UserOtherSessionsViewState(bindings: bindings, @@ -350,13 +356,13 @@ class UserOtherSessionsViewModelTests: XCTestCase { } private func createUserSessionInfo(sessionId: String, - isVerified: Bool = false, + verificationState: UserSessionInfo.VerificationState = .unverified, isActive: Bool = true, isCurrent: Bool = false) -> UserSessionInfo { UserSessionInfo(id: sessionId, name: "iOS", deviceType: .mobile, - verificationState: isVerified ? .verified : .unverified, + verificationState: verificationState, lastSeenIP: "10.0.0.10", lastSeenTimestamp: Date().timeIntervalSince1970 - 100, applicationName: nil, diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/UserOtherSessionsViewModel.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/UserOtherSessionsViewModel.swift index c82532dd21..84ea6f9ad1 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/UserOtherSessionsViewModel.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/UserOtherSessionsViewModel.swift @@ -172,7 +172,7 @@ private extension UserOtherSessionsFilter { case .inactive: return sessionInfos.filter { !$0.isActive } case .unverified: - return sessionInfos.filter { $0.verificationState != .verified } + return sessionInfos.filter { $0.verificationState.isUnverified } case .verified: return sessionInfos.filter { $0.verificationState == .verified } } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/MockUserSessionOverviewScreenState.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/MockUserSessionOverviewScreenState.swift index 6b7040a2c9..adb01d9ec2 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/MockUserSessionOverviewScreenState.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/MockUserSessionOverviewScreenState.swift @@ -39,6 +39,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable { .currentSession(sessionState: .verified), .otherSession(sessionState: .verified), .otherSession(sessionState: .unverified), + .otherSession(sessionState: .permanentlyUnverified), .sessionWithPushNotifications(enabled: true), .sessionWithPushNotifications(enabled: false), .remotelyTogglingPushersNotAvailable] diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift index af71412477..746fa38ba9 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift @@ -93,4 +93,9 @@ class UserSessionOverviewUITests: MockScreenTestCase { let button = app.buttons[buttonId] XCTAssertTrue(button.exists) } + + func test_whenPermanentlySessionSelected_copyIsCorrect() { + app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.otherSession(sessionState: .permanentlyUnverified).title) + XCTAssertTrue(app.buttons[VectorL10n.userOtherSessionPermanentlyUnverifiedAdditionalInfo].exists) + } } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift index 1028dd3cbf..a5c90d65dd 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift @@ -45,7 +45,9 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol { } func verificationState(for deviceInfo: MXDeviceInfo?) -> UserSessionInfo.VerificationState { - guard let deviceInfo = deviceInfo else { return .unknown } + guard let deviceInfo = deviceInfo else { + return .permanentlyUnverified + } guard session.crypto?.crossSigning.canCrossSign == true else { return deviceInfo.deviceId == session.myDeviceId ? .unverified : .unknown diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift index 666232a0cc..9869ecd1ba 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsOverviewService.swift @@ -117,7 +117,7 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol { private func sessionsOverviewData(from allSessions: [UserSessionInfo], linkDeviceEnabled: Bool) -> UserSessionsOverviewData { UserSessionsOverviewData(currentSession: allSessions.filter(\.isCurrent).first, - unverifiedSessions: allSessions.filter { $0.verificationState == .unverified && !$0.isCurrent }, + unverifiedSessions: allSessions.filter { $0.verificationState.isUnverified && !$0.isCurrent }, inactiveSessions: allSessions.filter { !$0.isActive }, otherSessions: allSessions.filter { !$0.isCurrent }, linkDeviceEnabled: linkDeviceEnabled) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift index 250283410c..227ed5d012 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift @@ -67,7 +67,7 @@ struct UserSessionListItemViewDataFactory { switch sessionInfo.verificationState { case .verified: sessionStatusText = VectorL10n.userSessionVerifiedShort - case .unverified: + case .unverified, .permanentlyUnverified: sessionStatusText = VectorL10n.userSessionUnverifiedShort case .unknown: sessionStatusText = nil diff --git a/RiotTests/UserSessionsDataProviderTests.swift b/RiotTests/UserSessionsDataProviderTests.swift index 3780dcd653..4695d9fc31 100644 --- a/RiotTests/UserSessionsDataProviderTests.swift +++ b/RiotTests/UserSessionsDataProviderTests.swift @@ -82,6 +82,24 @@ class UserSessionCardViewDataTests: XCTestCase { XCTAssertEqual(verificationStateVerified, .unverified) XCTAssertEqual(verificationStateUnverified, .unverified) } + + func testDeviceNotHavingCryptoSupportOnVerifiedDevice() { + let mxSession = MockSession(canCrossSign: true) + let dataProvider = UserSessionsDataProvider(session: mxSession) + + let verificationState = dataProvider.verificationState(for: nil) + + XCTAssertEqual(verificationState, .permanentlyUnverified) + } + + func testDeviceNotHavingCryptoSupportOnUnverifiedDevice() { + let mxSession = MockSession(canCrossSign: false) + let dataProvider = UserSessionsDataProvider(session: mxSession) + + let verificationState = dataProvider.verificationState(for: nil) + + XCTAssertEqual(verificationState, .permanentlyUnverified) + } } // MARK: Mocks diff --git a/changelog.d/pr-7083.change b/changelog.d/pr-7083.change new file mode 100644 index 0000000000..e9a3842d88 --- /dev/null +++ b/changelog.d/pr-7083.change @@ -0,0 +1 @@ +Add support in the new Device Manager to sessions without crypto support.