Posthog: report SessionSecurityState (#2795)

* Posthog: report SessionSecurityState

* Review quick change

---------

Co-authored-by: Mauro <34335419+Velin92@users.noreply.github.com>
This commit is contained in:
Valere
2024-05-07 16:31:12 +02:00
committed by GitHub
parent e829a3ded6
commit bd4fae6086
10 changed files with 157 additions and 9 deletions
+5 -1
View File
@@ -431,6 +431,7 @@
6860721DB3091BE08164C132 /* MapAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B48B7AD4908C5C374517B892 /* MapAssets.xcassets */; };
68AC3C84E2B438036B174E30 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */; };
695825D20A761C678809345D /* MessageForwardingScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */; };
69A9B430397C15075D86193F /* UserPropertiesExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */; };
69B3C6010B42010F591FC3CB /* RoomRolesAndPermissionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AF829F12FDC99717082D9 /* RoomRolesAndPermissionsScreenViewModel.swift */; };
69BCBB4FB2DC3D61A28D3FD8 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */; };
69C7B956B74BEC3DB88224EA /* NavigationSplitCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */; };
@@ -1533,6 +1534,7 @@
6663BFB9FDB8752562CD12CA /* AuthenticationStartScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenCoordinator.swift; sourceTree = "<group>"; };
667DD3A9D932D7D9EB380CAA /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sk; path = sk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadingPreprocessor.swift; sourceTree = "<group>"; };
66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertiesExt.swift; sourceTree = "<group>"; };
66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = "<group>"; };
6722709BD6178E10B70C9641 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SAS.strings; sourceTree = "<group>"; };
68010886142843705E342645 /* ProgressMaskModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressMaskModifier.swift; sourceTree = "<group>"; };
@@ -3120,6 +3122,7 @@
5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */,
1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */,
752A0EB49BF5BCEA37EDF7A3 /* Signposter.swift */,
66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */,
3A304097A59704AC9B869EC6 /* Helpers */,
);
path = Analytics;
@@ -6555,6 +6558,7 @@
D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */,
AC1DB27A4134470846BE49F6 /* UserProfileScreenViewModel.swift in Sources */,
46A183C6125A669AEB005699 /* UserProfileScreenViewModelProtocol.swift in Sources */,
69A9B430397C15075D86193F /* UserPropertiesExt.swift in Sources */,
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */,
4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */,
3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */,
@@ -7378,7 +7382,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-analytics-events";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 0.20.0;
minimumVersion = 0.21.0;
};
};
C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = {
@@ -121,8 +121,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-analytics-events",
"state" : {
"revision" : "e4e49896331c9dcf7346c90529a9aad7944a3259",
"version" : "0.20.0"
"revision" : "f10d044b9eaf35871bbb61cdfdb8f93a04e429e0",
"version" : "0.21.0"
}
},
{
@@ -203,6 +203,45 @@ class AnalyticsClientMock: AnalyticsClientProtocol {
screenReceivedInvocations.append(event)
screenClosure?(event)
}
//MARK: - updateUserProperties
var updateUserPropertiesUnderlyingCallsCount = 0
var updateUserPropertiesCallsCount: Int {
get {
if Thread.isMainThread {
return updateUserPropertiesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = updateUserPropertiesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
updateUserPropertiesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
updateUserPropertiesUnderlyingCallsCount = newValue
}
}
}
}
var updateUserPropertiesCalled: Bool {
return updateUserPropertiesCallsCount > 0
}
var updateUserPropertiesReceivedEvent: AnalyticsEvent.UserProperties?
var updateUserPropertiesReceivedInvocations: [AnalyticsEvent.UserProperties] = []
var updateUserPropertiesClosure: ((AnalyticsEvent.UserProperties) -> Void)?
func updateUserProperties(_ event: AnalyticsEvent.UserProperties) {
updateUserPropertiesCallsCount += 1
updateUserPropertiesReceivedEvent = event
updateUserPropertiesReceivedInvocations.append(event)
updateUserPropertiesClosure?(event)
}
}
class AppLockServiceMock: AppLockServiceProtocol {
var isMandatory: Bool {
@@ -14,6 +14,7 @@
// limitations under the License.
//
import AnalyticsEvents
import Combine
import SwiftUI
@@ -86,6 +87,21 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}
.store(in: &cancellables)
userSession.sessionSecurityStatePublisher
.receive(on: DispatchQueue.main)
.filter { state in
state.verificationState != .unknown
&& state.recoveryState != .settingUp
&& state.recoveryState != .unknown
}
.sink { [weak self] state in
guard let self else { return }
self.analyticsService.updateUserProperties(AnalyticsEvent.newVerificationStateUserProperty(verificationState: state.verificationState, recoveryState: state.recoveryState))
self.analyticsService.trackSessionSecurityState(state)
}
.store(in: &cancellables)
selectedRoomPublisher
.weakAssign(to: \.state.selectedRoomID, on: self)
.store(in: &cancellables)
@@ -38,6 +38,10 @@ protocol AnalyticsClientProtocol {
/// Capture the supplied analytics screen event.
/// - Parameter event: The screen event to capture.
func screen(_ event: AnalyticsScreenProtocol)
/// Updates the user properties
/// - Parameter userProperties: The properties event to capture.
func updateUserProperties(_ event: AnalyticsEvent.UserProperties)
}
// sourcery: AutoMockable
@@ -223,4 +223,39 @@ extension AnalyticsService {
let role = role.map(AnalyticsEvent.RoomModeration.Role.init)
capture(event: AnalyticsEvent.RoomModeration(action: action, role: role))
}
func trackSessionSecurityState(_ state: SessionSecurityState) {
let analyticsVerificationState: AnalyticsEvent.CryptoSessionStateChange.VerificationState
switch state.verificationState {
case .unknown:
return
case .verified:
analyticsVerificationState = .Verified
case .unverified:
analyticsVerificationState = .NotVerified
}
let analyticsRecoveryState: AnalyticsEvent.CryptoSessionStateChange.RecoveryState
switch state.recoveryState {
case .enabled:
analyticsRecoveryState = .Enabled
case .disabled:
analyticsRecoveryState = .Disabled
case .incomplete:
analyticsRecoveryState = .Incomplete
case .unknown:
return
case .settingUp:
return
}
let event = AnalyticsEvent.CryptoSessionStateChange(recoveryState: analyticsRecoveryState, verificationState: analyticsVerificationState)
client.capture(event)
}
func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) {
client.updateUserProperties(userProperties)
}
}
@@ -88,7 +88,9 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
self.pendingUserProperties = AnalyticsEvent.UserProperties(allChatsActiveFilter: userProperties.allChatsActiveFilter ?? pendingUserProperties.allChatsActiveFilter,
ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection,
numFavouriteRooms: userProperties.numFavouriteRooms ?? pendingUserProperties.numFavouriteRooms,
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces)
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces,
recoveryState: userProperties.recoveryState ?? pendingUserProperties.recoveryState,
verificationState: userProperties.verificationState ?? pendingUserProperties.verificationState)
}
func updateSuperProperties(_ updatedProperties: AnalyticsEvent.SuperProperties) {
@@ -0,0 +1,46 @@
//
// Copyright 2024 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 AnalyticsEvents
import Foundation
extension AnalyticsEvent {
static func newVerificationStateUserProperty(verificationState: SessionVerificationState, recoveryState: SecureBackupRecoveryState) -> UserProperties {
let analyticsVerificationState: AnalyticsEvent.UserProperties.VerificationState? = switch verificationState {
case .unknown:
nil
case .verified:
.Verified
case .unverified:
.NotVerified
}
let analyticsRecoveryState: AnalyticsEvent.UserProperties.RecoveryState? = switch recoveryState {
case .enabled:
.Enabled
case .disabled:
.Disabled
case .incomplete:
.Incomplete
case .unknown:
nil
case .settingUp:
nil
}
return UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: nil, numFavouriteRooms: nil, numSpaces: nil, recoveryState: analyticsRecoveryState, verificationState: analyticsVerificationState)
}
}
+6 -4
View File
@@ -142,13 +142,15 @@ class AnalyticsTests: XCTestCase {
client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil,
ftueUseCaseSelection: .PersonalMessaging,
numFavouriteRooms: 4,
numSpaces: 5))
numSpaces: 5, recoveryState: .Disabled, verificationState: .Verified))
// Then the properties should be cached
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.")
XCTAssertEqual(client.pendingUserProperties?.numFavouriteRooms, 4, "The number of favorite rooms should match.")
XCTAssertEqual(client.pendingUserProperties?.numSpaces, 5, "The number of spaces should match.")
XCTAssertEqual(client.pendingUserProperties?.verificationState, AnalyticsEvent.UserProperties.VerificationState.Verified, "The verification state should match.")
XCTAssertEqual(client.pendingUserProperties?.recoveryState, AnalyticsEvent.UserProperties.RecoveryState.Disabled, "The recovery state should match.")
}
func testMergingUserProperties() {
@@ -156,7 +158,7 @@ class AnalyticsTests: XCTestCase {
let client = PostHogAnalyticsClient()
client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: .PersonalMessaging,
numFavouriteRooms: nil,
numSpaces: nil))
numSpaces: nil, recoveryState: nil, verificationState: nil))
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.")
@@ -166,7 +168,7 @@ class AnalyticsTests: XCTestCase {
// When updating the number of spaced
client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: nil,
numFavouriteRooms: 4,
numSpaces: 5))
numSpaces: 5, recoveryState: nil, verificationState: nil))
// Then the new properties should be updated and the existing properties should remain unchanged
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
@@ -183,7 +185,7 @@ class AnalyticsTests: XCTestCase {
client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: .PersonalMessaging,
numFavouriteRooms: nil,
numSpaces: nil))
numSpaces: nil, recoveryState: nil, verificationState: nil))
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.")
+1 -1
View File
@@ -57,7 +57,7 @@ packages:
# path: ../compound-ios
AnalyticsEvents:
url: https://github.com/matrix-org/matrix-analytics-events
minorVersion: 0.20.0
minorVersion: 0.21.0
# path: ../matrix-analytics-events
Emojibase:
url: https://github.com/matrix-org/emojibase-bindings