mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Clean up the Signposter implementation and align on new metrics
This commit is contained in:
committed by
Stefan Ceriu
parent
8b768f74f8
commit
a2a8527ffc
@@ -76,6 +76,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
appMediator = AppMediator(windowManager: windowManager, networkMonitor: networkMonitor)
|
appMediator = AppMediator(windowManager: windowManager, networkMonitor: networkMonitor)
|
||||||
|
|
||||||
let appSettings = appHooks.appSettingsHook.configure(AppSettings())
|
let appSettings = appHooks.appSettingsHook.configure(AppSettings())
|
||||||
|
ServiceLocator.shared.register(appSettings: appSettings)
|
||||||
|
|
||||||
targetConfiguration = Target.mainApp.configure(logLevel: appSettings.logLevel,
|
targetConfiguration = Target.mainApp.configure(logLevel: appSettings.logLevel,
|
||||||
traceLogPacks: appSettings.traceLogPacks,
|
traceLogPacks: appSettings.traceLogPacks,
|
||||||
@@ -95,8 +96,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
self.appDelegate = appDelegate
|
self.appDelegate = appDelegate
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
self.appHooks = appHooks
|
self.appHooks = appHooks
|
||||||
|
|
||||||
appRouteURLParser = AppRouteURLParser(appSettings: appSettings)
|
appRouteURLParser = AppRouteURLParser(appSettings: appSettings)
|
||||||
|
|
||||||
|
ServiceLocator.shared.register(userIndicatorController: UserIndicatorController())
|
||||||
|
|
||||||
|
let posthogAnalyticsClient = PostHogAnalyticsClient()
|
||||||
|
posthogAnalyticsClient.updateSuperProperties(AnalyticsEvent.SuperProperties(appPlatform: .EXI, cryptoSDK: .Rust, cryptoSDKVersion: sdkGitSha()))
|
||||||
|
let analyticsService = AnalyticsService(client: posthogAnalyticsClient, appSettings: appSettings)
|
||||||
|
ServiceLocator.shared.register(analytics: analyticsService)
|
||||||
|
|
||||||
elementCallService = ElementCallService()
|
elementCallService = ElementCallService()
|
||||||
|
|
||||||
navigationRootCoordinator = NavigationRootCoordinator()
|
navigationRootCoordinator = NavigationRootCoordinator()
|
||||||
@@ -107,7 +116,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
|
|
||||||
let keychainController = KeychainController(service: .sessions,
|
let keychainController = KeychainController(service: .sessions,
|
||||||
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||||
userSessionStore = UserSessionStore(keychainController: keychainController, appSettings: appSettings, appHooks: appHooks, networkMonitor: networkMonitor)
|
userSessionStore = UserSessionStore(keychainController: keychainController,
|
||||||
|
appSettings: appSettings,
|
||||||
|
analyticsService: analyticsService,
|
||||||
|
appHooks: appHooks,
|
||||||
|
networkMonitor: networkMonitor)
|
||||||
|
|
||||||
let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings)
|
let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings)
|
||||||
let appLockNavigationCoordinator = NavigationRootCoordinator()
|
let appLockNavigationCoordinator = NavigationRootCoordinator()
|
||||||
@@ -123,11 +136,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
sdkGitSHA: sdkGitSha(),
|
sdkGitSHA: sdkGitSha(),
|
||||||
maxUploadSize: appSettings.bugReportMaxUploadSize,
|
maxUploadSize: appSettings.bugReportMaxUploadSize,
|
||||||
appHooks: appHooks)
|
appHooks: appHooks)
|
||||||
Self.setupServiceLocator(appSettings: appSettings, appHooks: appHooks)
|
|
||||||
Self.setupSentry(bugReportService: bugReportService, appSettings: appSettings)
|
Self.setupSentry(bugReportService: bugReportService, appSettings: appSettings)
|
||||||
|
|
||||||
ServiceLocator.shared.analytics.signpost.start()
|
analyticsService.startIfEnabled()
|
||||||
ServiceLocator.shared.analytics.startIfEnabled()
|
|
||||||
|
|
||||||
windowManager.delegate = self
|
windowManager.delegate = self
|
||||||
|
|
||||||
@@ -356,6 +368,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
} else if appSettings.threadsEnabled, let threadRootEventID = content.threadRootEventID {
|
} else if appSettings.threadsEnabled, let threadRootEventID = content.threadRootEventID {
|
||||||
handleAppRoute(.thread(roomID: roomID, threadRootEventID: threadRootEventID, focusEventID: eventID))
|
handleAppRoute(.thread(roomID: roomID, threadRootEventID: threadRootEventID, focusEventID: eventID))
|
||||||
} else if let eventID {
|
} else if let eventID {
|
||||||
|
// Only track main timeline event deeplinking
|
||||||
|
ServiceLocator.shared.analytics.signpost.startTransaction(.notificationToMessage)
|
||||||
handleAppRoute(.event(eventID: eventID, roomID: roomID, via: []))
|
handleAppRoute(.event(eventID: eventID, roomID: roomID, via: []))
|
||||||
} else {
|
} else {
|
||||||
handleAppRoute(.room(roomID: roomID, via: []))
|
handleAppRoute(.room(roomID: roomID, via: []))
|
||||||
@@ -380,16 +394,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private static func setupServiceLocator(appSettings: AppSettings, appHooks: AppHooks) {
|
|
||||||
ServiceLocator.shared.register(userIndicatorController: UserIndicatorController())
|
|
||||||
ServiceLocator.shared.register(appSettings: appSettings)
|
|
||||||
|
|
||||||
let posthogAnalyticsClient = PostHogAnalyticsClient()
|
|
||||||
posthogAnalyticsClient.updateSuperProperties(AnalyticsEvent.SuperProperties(appPlatform: .EXI, cryptoSDK: .Rust, cryptoSDKVersion: sdkGitSha()))
|
|
||||||
ServiceLocator.shared.register(analytics: AnalyticsService(client: posthogAnalyticsClient,
|
|
||||||
appSettings: appSettings))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform any required migrations for the app to function correctly.
|
/// Perform any required migrations for the app to function correctly.
|
||||||
private func performMigrationsIfNecessary(from oldVersion: Version, to newVersion: Version) {
|
private func performMigrationsIfNecessary(from oldVersion: Version, to newVersion: Version) {
|
||||||
guard oldVersion != newVersion else { return }
|
guard oldVersion != newVersion else { return }
|
||||||
@@ -693,6 +697,14 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
fatalError("User session not setup")
|
fatalError("User session not setup")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let serverName = userSession.clientProxy.userIDServerName {
|
||||||
|
ServiceLocator.shared.analytics.signpost.addGlobalTag(.homeserver, value: serverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isNewLogin {
|
||||||
|
ServiceLocator.shared.analytics.signpost.startTransaction(.cachedRoomList)
|
||||||
|
}
|
||||||
|
|
||||||
let flowParameters = CommonFlowParameters(userSession: userSession,
|
let flowParameters = CommonFlowParameters(userSession: userSession,
|
||||||
bugReportService: bugReportService,
|
bugReportService: bugReportService,
|
||||||
elementCallService: elementCallService,
|
elementCallService: elementCallService,
|
||||||
@@ -1061,9 +1073,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
private func startSync() {
|
private func startSync() {
|
||||||
guard let userSession else { return }
|
guard let userSession else { return }
|
||||||
|
|
||||||
let serverName = String(userSession.clientProxy.userIDServerName ?? "Unknown")
|
ServiceLocator.shared.analytics.signpost.startTransaction(.upToDateRoomList)
|
||||||
|
|
||||||
ServiceLocator.shared.analytics.signpost.beginFirstSync(serverName: serverName)
|
|
||||||
userSession.clientProxy.startSync()
|
userSession.clientProxy.startSync()
|
||||||
|
|
||||||
guard clientProxyObserver == nil else {
|
guard clientProxyObserver == nil else {
|
||||||
@@ -1072,6 +1083,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
|
|
||||||
clientProxyObserver = userSession.clientProxy
|
clientProxyObserver = userSession.clientProxy
|
||||||
.loadingStatePublisher
|
.loadingStatePublisher
|
||||||
|
.dropFirst()
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
@@ -1084,7 +1096,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: toastIdentifier, type: .toast(progress: .indeterminate), title: L10n.commonSyncing, persistent: true))
|
ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: toastIdentifier, type: .toast(progress: .indeterminate), title: L10n.commonSyncing, persistent: true))
|
||||||
}
|
}
|
||||||
case .notLoading:
|
case .notLoading:
|
||||||
ServiceLocator.shared.analytics.signpost.endFirstSync()
|
ServiceLocator.shared.analytics.signpost.finishTransaction(.upToDateRoomList)
|
||||||
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(toastIdentifier)
|
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(toastIdentifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,8 +111,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
self.flowParameters = flowParameters
|
self.flowParameters = flowParameters
|
||||||
|
|
||||||
setupStateMachine()
|
setupStateMachine()
|
||||||
|
|
||||||
flowParameters.analytics.signpost.beginRoomFlow(roomID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - FlowCoordinatorProtocol
|
// MARK: - FlowCoordinatorProtocol
|
||||||
@@ -129,6 +127,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch appRoute {
|
switch appRoute {
|
||||||
case .room(let roomID, let via):
|
case .room(let roomID, let via):
|
||||||
|
flowParameters.analytics.signpost.startTransaction(.openRoom)
|
||||||
Task {
|
Task {
|
||||||
await handleRoomRoute(roomID: roomID, via: via, animated: animated)
|
await handleRoomRoute(roomID: roomID, via: via, animated: animated)
|
||||||
}
|
}
|
||||||
@@ -897,7 +896,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
} else {
|
} else {
|
||||||
actionsSubject.send(.finished)
|
actionsSubject.send(.finished)
|
||||||
}
|
}
|
||||||
flowParameters.analytics.signpost.endRoomFlow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentRoomDetails(isRoot: Bool, animated: Bool) async {
|
private func presentRoomDetails(isRoot: Bool, animated: Bool) async {
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ extension SDKListener: SendQueueRoomErrorListener where T == (String, ClientErro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SDKListener: SendQueueRoomUpdateListener where T == (String, RoomSendQueueUpdate) {
|
||||||
|
func onUpdate(roomId: String, update: RoomSendQueueUpdate) {
|
||||||
|
onUpdateClosure((roomId, update))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: SecureBackupController
|
// MARK: SecureBackupController
|
||||||
|
|
||||||
extension SDKListener: BackupStateListener where T == BackupState {
|
extension SDKListener: BackupStateListener where T == BackupState {
|
||||||
|
|||||||
@@ -95,18 +95,15 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
|
|||||||
startLoading(isInteractionBlocking: true)
|
startLoading(isInteractionBlocking: true)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
analytics.signpost.beginLogin()
|
|
||||||
switch await authenticationService.login(username: state.bindings.username,
|
switch await authenticationService.login(username: state.bindings.username,
|
||||||
password: state.bindings.password,
|
password: state.bindings.password,
|
||||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||||
deviceID: nil) {
|
deviceID: nil) {
|
||||||
case .success(let userSession):
|
case .success(let userSession):
|
||||||
actionsSubject.send(.signedIn(userSession))
|
actionsSubject.send(.signedIn(userSession))
|
||||||
analytics.signpost.endLogin()
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
stopLoading()
|
stopLoading()
|
||||||
analytics.signpost.endLogin()
|
|
||||||
handleError(error)
|
handleError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,8 +292,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsService.signpost.beginFirstRooms()
|
|
||||||
|
|
||||||
roomSummaryProvider.statePublisher
|
roomSummaryProvider.statePublisher
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
@@ -329,7 +327,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
if roomListMode == .rooms, state.roomListMode == .skeletons {
|
if roomListMode == .rooms, state.roomListMode == .skeletons {
|
||||||
analyticsService.signpost.endFirstRooms()
|
analyticsService.signpost.finishTransaction(.cachedRoomList)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.roomListMode = roomListMode
|
state.roomListMode = roomListMode
|
||||||
|
|||||||
@@ -256,7 +256,9 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showFocusLoadingIndicator()
|
showFocusLoadingIndicator()
|
||||||
defer { hideFocusLoadingIndicator() }
|
defer {
|
||||||
|
hideFocusLoadingIndicator()
|
||||||
|
}
|
||||||
|
|
||||||
switch await timelineController.focusOnEvent(eventID, timelineSize: Constants.detachedTimelineSize) {
|
switch await timelineController.focusOnEvent(eventID, timelineSize: Constants.detachedTimelineSize) {
|
||||||
case .success:
|
case .success:
|
||||||
@@ -318,6 +320,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
focussedEvent.appearance = .hasAppeared
|
focussedEvent.appearance = .hasAppeared
|
||||||
state.timelineState.focussedEvent = focussedEvent
|
state.timelineState.focussedEvent = focussedEvent
|
||||||
hideFocusLoadingIndicator()
|
hideFocusLoadingIndicator()
|
||||||
|
analyticsService.signpost.finishTransaction(.notificationToMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +430,10 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
switch callback {
|
switch callback {
|
||||||
case .updatedTimelineItems(let updatedItems, let isSwitchingTimelines):
|
case .updatedTimelineItems(let updatedItems, let isSwitchingTimelines):
|
||||||
buildTimelineViews(timelineItems: updatedItems, isSwitchingTimelines: isSwitchingTimelines)
|
buildTimelineViews(timelineItems: updatedItems, isSwitchingTimelines: isSwitchingTimelines)
|
||||||
|
|
||||||
|
if !updatedItems.isEmpty {
|
||||||
|
analyticsService.signpost.finishTransaction(.openRoom)
|
||||||
|
}
|
||||||
case .paginationState(let paginationState):
|
case .paginationState(let paginationState):
|
||||||
if state.timelineState.paginationState != paginationState {
|
if state.timelineState.paginationState != paginationState {
|
||||||
state.timelineState.paginationState = paginationState
|
state.timelineState.paginationState = paginationState
|
||||||
|
|||||||
@@ -6,140 +6,106 @@
|
|||||||
// Please see LICENSE files in the repository root for full details.
|
// Please see LICENSE files in the repository root for full details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import OSLog
|
import CryptoKit
|
||||||
import Sentry
|
import Sentry
|
||||||
|
|
||||||
/// A simple wrapper around OSSignposter for easy performance testing.
|
/// A simple wrapper around Sentry for easy instrumentation
|
||||||
class Signposter {
|
class Signposter {
|
||||||
/// The underlying signposter.
|
private var transactions = [TransactionName: any Sentry.Span]()
|
||||||
private let signposter = OSSignposter(subsystem: subsystem, category: category)
|
|
||||||
/// A logger instance to capture any errors.
|
|
||||||
private let logger = Logger(subsystem: subsystem, category: category)
|
|
||||||
|
|
||||||
/// Signpost name constants.
|
private var globalTags = [TagName: String]()
|
||||||
enum Name {
|
|
||||||
static let login: StaticString = "Login"
|
|
||||||
static let firstSync: StaticString = "FirstSync"
|
|
||||||
static let firstRooms: StaticString = "FirstRooms"
|
|
||||||
static let roomFlow: StaticString = "RoomFlow"
|
|
||||||
|
|
||||||
static let appStartup = "AppStartup"
|
enum TransactionName: Hashable {
|
||||||
static let appStarted = "AppStarted"
|
case cachedRoomList
|
||||||
|
case upToDateRoomList
|
||||||
|
case notificationToMessage
|
||||||
|
case openRoom
|
||||||
|
case sendMessage(uuid: String)
|
||||||
|
|
||||||
static let homeserver = "homeserver"
|
var id: String {
|
||||||
|
switch self {
|
||||||
|
case .cachedRoomList:
|
||||||
|
"Cached room list"
|
||||||
|
case .upToDateRoomList:
|
||||||
|
"Up-to-date room list"
|
||||||
|
case .notificationToMessage:
|
||||||
|
"Notification to message"
|
||||||
|
case .openRoom:
|
||||||
|
"Open a room"
|
||||||
|
case .sendMessage:
|
||||||
|
"Send a message"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static let subsystem = "ElementX"
|
enum SpanName: String {
|
||||||
static let category = "PerformanceTests"
|
case timelineLoad = "Timeline load"
|
||||||
|
|
||||||
// MARK: - App Startup
|
|
||||||
|
|
||||||
private var appStartupTransaction: Span?
|
|
||||||
|
|
||||||
/// We have a manual start method because we need to configure the ServiceLocator *before* we configure
|
|
||||||
/// Sentry but this class is created in the AnalyticsService and so spans and transactions are dropped
|
|
||||||
/// until Sentry has been configured. Therefore doing this in the init doesn't work.
|
|
||||||
func start() {
|
|
||||||
appStartupTransaction = SentrySDK.startTransaction(name: Name.appStartup, operation: Name.appStarted)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Login
|
struct Span {
|
||||||
|
fileprivate let innerSpan: Sentry.Span
|
||||||
|
|
||||||
private var loginState: OSSignpostIntervalState?
|
func finish() {
|
||||||
private var loginTransaction: Span?
|
innerSpan.finish()
|
||||||
private var loginSpan: Span?
|
}
|
||||||
|
|
||||||
func beginLogin() {
|
|
||||||
loginState = signposter.beginInterval(Name.login)
|
|
||||||
loginTransaction = SentrySDK.startTransaction(name: "\(Name.login)", operation: "\(Name.login)")
|
|
||||||
loginSpan = appStartupTransaction?.startChild(operation: "\(Name.login)", description: "\(Name.login)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func endLogin() {
|
enum TagName: String {
|
||||||
guard let loginState else {
|
case homeserver = "Homeserver"
|
||||||
logger.error("Missing login state.")
|
}
|
||||||
return
|
|
||||||
|
// MARK: - Transactions
|
||||||
|
|
||||||
|
func startTransaction(_ transactionName: TransactionName, operation: String = "ux", tags: [TagName: String] = [:]) {
|
||||||
|
let span = SentrySDK.startTransaction(name: transactionName.id, operation: operation)
|
||||||
|
|
||||||
|
tags
|
||||||
|
.merging(globalTags) { tagValue, _ in
|
||||||
|
tagValue
|
||||||
|
}
|
||||||
|
.forEach { (key: TagName, value: String) in
|
||||||
|
span.setTag(value: value, key: key.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions[transactionName] = span
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishTransaction(_ transactionName: TransactionName) {
|
||||||
|
transactions[transactionName]?.finish()
|
||||||
|
transactions[transactionName] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Spans
|
||||||
|
|
||||||
|
func addSpan(_ spanName: SpanName, toTransaction transactionName: TransactionName) -> Span? {
|
||||||
|
guard let transaction = transactions[transactionName] else {
|
||||||
|
MXLog.error("Transaction not started or already finished")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signposter.endInterval(Name.login, loginState)
|
return Span(innerSpan: transaction.startChild(operation: spanName.rawValue))
|
||||||
loginTransaction?.finish()
|
|
||||||
loginSpan?.finish()
|
|
||||||
|
|
||||||
self.loginState = nil
|
|
||||||
loginTransaction = nil
|
|
||||||
loginSpan = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - FirstSync
|
// MARK: - Tags
|
||||||
|
|
||||||
private var firstSyncState: OSSignpostIntervalState?
|
func addGlobalTag(_ tagName: TagName, value: String) {
|
||||||
private var firstSyncTransaction: Span?
|
let value = switch tagName {
|
||||||
private var firstSyncSpan: Span?
|
case .homeserver:
|
||||||
|
sha512(value)
|
||||||
func beginFirstSync(serverName: String) {
|
|
||||||
appStartupTransaction?.setTag(value: serverName, key: Name.homeserver)
|
|
||||||
|
|
||||||
firstSyncState = signposter.beginInterval(Name.firstSync)
|
|
||||||
|
|
||||||
firstSyncTransaction = SentrySDK.startTransaction(name: "\(Name.firstSync)", operation: "\(Name.firstSync)")
|
|
||||||
firstSyncTransaction?.setTag(value: serverName, key: Name.homeserver)
|
|
||||||
|
|
||||||
firstSyncSpan = appStartupTransaction?.startChild(operation: "\(Name.firstSync)", description: "\(Name.firstSync)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func endFirstSync() {
|
|
||||||
guard let firstSyncState else { return }
|
|
||||||
|
|
||||||
signposter.endInterval(Name.firstSync, firstSyncState)
|
|
||||||
firstSyncTransaction?.finish()
|
|
||||||
firstSyncSpan?.finish()
|
|
||||||
|
|
||||||
self.firstSyncState = nil
|
|
||||||
firstSyncTransaction = nil
|
|
||||||
firstSyncSpan = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - FirstRooms
|
|
||||||
|
|
||||||
private var firstRoomsState: OSSignpostIntervalState?
|
|
||||||
private var firstRoomsTransaction: Span?
|
|
||||||
private var firstRoomsSpan: Span?
|
|
||||||
|
|
||||||
func beginFirstRooms() {
|
|
||||||
firstRoomsState = signposter.beginInterval(Name.firstRooms)
|
|
||||||
firstRoomsTransaction = SentrySDK.startTransaction(name: "\(Name.firstRooms)", operation: "\(Name.firstRooms)")
|
|
||||||
firstRoomsSpan = appStartupTransaction?.startChild(operation: "\(Name.firstRooms)", description: "\(Name.firstRooms)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func endFirstRooms() {
|
|
||||||
defer {
|
|
||||||
appStartupTransaction?.finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let firstRoomsState else { return }
|
globalTags[tagName] = value
|
||||||
|
|
||||||
signposter.endInterval(Name.firstRooms, firstRoomsState)
|
|
||||||
firstRoomsTransaction?.finish()
|
|
||||||
firstRoomsSpan?.finish()
|
|
||||||
|
|
||||||
self.firstRoomsState = nil
|
|
||||||
firstRoomsTransaction = nil
|
|
||||||
firstRoomsSpan = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Room Flow
|
func removeGlobalTag(_ tagName: TagName) {
|
||||||
|
globalTags[tagName] = nil
|
||||||
private var roomFlowState: OSSignpostIntervalState?
|
|
||||||
|
|
||||||
func beginRoomFlow(_ name: String) {
|
|
||||||
roomFlowState = signposter.beginInterval(Name.roomFlow)
|
|
||||||
signposter.emitEvent("RoomName", "\(name, privacy: .auto(mask: .hash))")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func endRoomFlow() {
|
// MARK: - Private
|
||||||
guard let roomFlowState else { return }
|
|
||||||
|
|
||||||
signposter.endInterval(Name.roomFlow, roomFlowState)
|
func sha512(_ string: String) -> String {
|
||||||
self.roomFlowState = nil
|
let data = Data(string.utf8)
|
||||||
|
let hash = SHA512.hash(data: data)
|
||||||
|
return hash.map { String(format: "%02x", $0) }.joined()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import Foundation
|
|||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
import OrderedCollections
|
import OrderedCollections
|
||||||
|
|
||||||
|
// swiftlint:disable:next type_body_length
|
||||||
class ClientProxy: ClientProxyProtocol {
|
class ClientProxy: ClientProxyProtocol {
|
||||||
private let client: ClientProtocol
|
private let client: ClientProtocol
|
||||||
private let networkMonitor: NetworkMonitorProtocol
|
private let networkMonitor: NetworkMonitorProtocol
|
||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
|
private let analyticsService: AnalyticsService
|
||||||
|
|
||||||
let mediaLoader: MediaLoaderProtocol
|
let mediaLoader: MediaLoaderProtocol
|
||||||
private let clientQueue: DispatchQueue
|
private let clientQueue: DispatchQueue
|
||||||
@@ -37,7 +39,10 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
private var verificationStateListenerTaskHandle: TaskHandle?
|
private var verificationStateListenerTaskHandle: TaskHandle?
|
||||||
|
|
||||||
// periphery:ignore - required for instance retention in the rust codebase
|
// periphery:ignore - required for instance retention in the rust codebase
|
||||||
private var sendQueueListenerTaskHandle: TaskHandle?
|
private var sendQueueStatusListenerTaskHandle: TaskHandle?
|
||||||
|
|
||||||
|
// periphery:ignore - required for instance retention in the rust codebase
|
||||||
|
private var sendQueueUpdatesListenerTaskHandle: TaskHandle?
|
||||||
|
|
||||||
// periphery:ignore - required for instance retention in the rust codebase
|
// periphery:ignore - required for instance retention in the rust codebase
|
||||||
private var mediaPreviewConfigListenerTaskHandle: TaskHandle?
|
private var mediaPreviewConfigListenerTaskHandle: TaskHandle?
|
||||||
@@ -184,10 +189,12 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
|
|
||||||
init(client: ClientProtocol,
|
init(client: ClientProtocol,
|
||||||
networkMonitor: NetworkMonitorProtocol,
|
networkMonitor: NetworkMonitorProtocol,
|
||||||
appSettings: AppSettings) async throws {
|
appSettings: AppSettings,
|
||||||
|
analyticsService: AnalyticsService) async throws {
|
||||||
self.client = client
|
self.client = client
|
||||||
self.networkMonitor = networkMonitor
|
self.networkMonitor = networkMonitor
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
|
self.analyticsService = analyticsService
|
||||||
|
|
||||||
clientQueue = .init(label: "ClientProxyQueue", attributes: .concurrent)
|
clientQueue = .init(label: "ClientProxyQueue", attributes: .concurrent)
|
||||||
|
|
||||||
@@ -243,11 +250,22 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
Task { await self?.updateVerificationState(verificationState) }
|
Task { await self?.updateVerificationState(verificationState) }
|
||||||
})
|
})
|
||||||
|
|
||||||
sendQueueListenerTaskHandle = client.subscribeToSendQueueStatus(listener: SDKListener { [weak self] roomID, error in
|
sendQueueStatusListenerTaskHandle = client.subscribeToSendQueueStatus(listener: SDKListener { [weak self] roomID, error in
|
||||||
MXLog.error("Send queue failed in room: \(roomID) with error: \(error)")
|
MXLog.error("Send queue failed in room: \(roomID) with error: \(error)")
|
||||||
self?.sendQueueStatusSubject.send(false)
|
self?.sendQueueStatusSubject.send(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
sendQueueUpdatesListenerTaskHandle = try? await client.subscribeToSendQueueUpdates(listener: SDKListener { _, update in
|
||||||
|
switch update {
|
||||||
|
case .newLocalEvent(let transactionID):
|
||||||
|
analyticsService.signpost.startTransaction(.sendMessage(uuid: transactionID))
|
||||||
|
case .sentEvent(let transactionID, _):
|
||||||
|
analyticsService.signpost.finishTransaction(.sendMessage(uuid: transactionID))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
sendQueueStatusSubject
|
sendQueueStatusSubject
|
||||||
.combineLatest(homeserverReachabilityPublisher)
|
.combineLatest(homeserverReachabilityPublisher)
|
||||||
.debounce(for: 1.0, scheduler: DispatchQueue.main)
|
.debounce(for: 1.0, scheduler: DispatchQueue.main)
|
||||||
@@ -1136,7 +1154,8 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
case .joined:
|
case .joined:
|
||||||
let roomProxy = try await JoinedRoomProxy(roomListService: roomListService,
|
let roomProxy = try await JoinedRoomProxy(roomListService: roomListService,
|
||||||
room: room,
|
room: room,
|
||||||
appSettings: appSettings)
|
appSettings: appSettings,
|
||||||
|
analyticsService: analyticsService)
|
||||||
|
|
||||||
return .joined(roomProxy)
|
return .joined(roomProxy)
|
||||||
case .left:
|
case .left:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
private let roomListService: RoomListServiceProtocol
|
private let roomListService: RoomListServiceProtocol
|
||||||
private let room: RoomProtocol
|
private let room: RoomProtocol
|
||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
|
private let analyticsService: AnalyticsService
|
||||||
|
|
||||||
// periphery:ignore - required for instance retention in the rust codebase
|
// periphery:ignore - required for instance retention in the rust codebase
|
||||||
private var roomInfoObservationToken: TaskHandle?
|
private var roomInfoObservationToken: TaskHandle?
|
||||||
@@ -76,13 +77,16 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
|
|
||||||
init(roomListService: RoomListServiceProtocol,
|
init(roomListService: RoomListServiceProtocol,
|
||||||
room: RoomProtocol,
|
room: RoomProtocol,
|
||||||
appSettings: AppSettings) async throws {
|
appSettings: AppSettings,
|
||||||
|
analyticsService: AnalyticsService) async throws {
|
||||||
self.roomListService = roomListService
|
self.roomListService = roomListService
|
||||||
self.room = room
|
self.room = room
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
|
self.analyticsService = analyticsService
|
||||||
|
|
||||||
infoSubject = try await .init(RoomInfoProxy(roomInfo: room.roomInfo()))
|
infoSubject = try await .init(RoomInfoProxy(roomInfo: room.roomInfo()))
|
||||||
|
|
||||||
|
let openRoomSpan = analyticsService.signpost.addSpan(.timelineLoad, toTransaction: .openRoom)
|
||||||
timeline = try await TimelineProxy(timeline: room.timelineWithConfiguration(configuration: .init(focus: .live(hideThreadedEvents: appSettings.threadsEnabled),
|
timeline = try await TimelineProxy(timeline: room.timelineWithConfiguration(configuration: .init(focus: .live(hideThreadedEvents: appSettings.threadsEnabled),
|
||||||
filter: .eventTypeFilter(filter: excludedEventsFilter),
|
filter: .eventTypeFilter(filter: excludedEventsFilter),
|
||||||
internalIdPrefix: nil,
|
internalIdPrefix: nil,
|
||||||
@@ -90,6 +94,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
trackReadReceipts: .messageLikeEvents,
|
trackReadReceipts: .messageLikeEvents,
|
||||||
reportUtds: true)),
|
reportUtds: true)),
|
||||||
kind: .live)
|
kind: .live)
|
||||||
|
openRoomSpan?.finish()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
await updateMembers()
|
await updateMembers()
|
||||||
@@ -146,6 +151,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
|
|
||||||
func timelineFocusedOnEvent(eventID: String, numberOfEvents: UInt16) async -> Result<TimelineProxyProtocol, RoomProxyError> {
|
func timelineFocusedOnEvent(eventID: String, numberOfEvents: UInt16) async -> Result<TimelineProxyProtocol, RoomProxyError> {
|
||||||
do {
|
do {
|
||||||
|
let openRoomSpan = analyticsService.signpost.addSpan(.timelineLoad, toTransaction: .notificationToMessage)
|
||||||
let sdkTimeline = try await room.timelineWithConfiguration(configuration: .init(focus: .event(eventId: eventID,
|
let sdkTimeline = try await room.timelineWithConfiguration(configuration: .init(focus: .event(eventId: eventID,
|
||||||
numContextEvents: numberOfEvents,
|
numContextEvents: numberOfEvents,
|
||||||
hideThreadedEvents: appSettings.threadsEnabled),
|
hideThreadedEvents: appSettings.threadsEnabled),
|
||||||
@@ -154,6 +160,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
|||||||
dateDividerMode: .daily,
|
dateDividerMode: .daily,
|
||||||
trackReadReceipts: .disabled,
|
trackReadReceipts: .disabled,
|
||||||
reportUtds: true))
|
reportUtds: true))
|
||||||
|
openRoomSpan?.finish()
|
||||||
|
|
||||||
return .success(TimelineProxy(timeline: sdkTimeline, kind: .detached))
|
return .success(TimelineProxy(timeline: sdkTimeline, kind: .detached))
|
||||||
} catch let error as FocusEventError {
|
} catch let error as FocusEventError {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import MatrixRustSDK
|
|||||||
class UserSessionStore: UserSessionStoreProtocol {
|
class UserSessionStore: UserSessionStoreProtocol {
|
||||||
private let keychainController: KeychainControllerProtocol
|
private let keychainController: KeychainControllerProtocol
|
||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
|
private let analyticsService: AnalyticsService
|
||||||
private let networkMonitor: NetworkMonitorProtocol
|
private let networkMonitor: NetworkMonitorProtocol
|
||||||
private let appHooks: AppHooks
|
private let appHooks: AppHooks
|
||||||
|
|
||||||
@@ -31,10 +32,12 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
|
|
||||||
init(keychainController: KeychainControllerProtocol,
|
init(keychainController: KeychainControllerProtocol,
|
||||||
appSettings: AppSettings,
|
appSettings: AppSettings,
|
||||||
|
analyticsService: AnalyticsService,
|
||||||
appHooks: AppHooks,
|
appHooks: AppHooks,
|
||||||
networkMonitor: NetworkMonitorProtocol) {
|
networkMonitor: NetworkMonitorProtocol) {
|
||||||
self.keychainController = keychainController
|
self.keychainController = keychainController
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
|
self.analyticsService = analyticsService
|
||||||
self.appHooks = appHooks
|
self.appHooks = appHooks
|
||||||
self.networkMonitor = networkMonitor
|
self.networkMonitor = networkMonitor
|
||||||
}
|
}
|
||||||
@@ -159,7 +162,8 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
do {
|
do {
|
||||||
return try await ClientProxy(client: client,
|
return try await ClientProxy(client: client,
|
||||||
networkMonitor: networkMonitor,
|
networkMonitor: networkMonitor,
|
||||||
appSettings: appSettings)
|
appSettings: appSettings,
|
||||||
|
analyticsService: analyticsService)
|
||||||
} catch {
|
} catch {
|
||||||
throw UserSessionStoreError.failedSettingUpClientProxy(error)
|
throw UserSessionStoreError.failedSettingUpClientProxy(error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user