diff --git a/Riot/Modules/Analytics/DecryptionFailureTracker.m b/Riot/Modules/Analytics/DecryptionFailureTracker.m index 2c79fdaf3..d45c9e64f 100644 --- a/Riot/Modules/Analytics/DecryptionFailureTracker.m +++ b/Riot/Modules/Analytics/DecryptionFailureTracker.m @@ -199,7 +199,7 @@ NSString *const kDecryptionFailureTrackerAnalyticsCategory = @"e2e.failure"; for (DecryptionFailure *failure in failuresToTrack) { if (failure.reason == DecryptionFailureReasonUnspecified) { - [BWIAnalytics.sharedTracker trackE2EEErrorWithDimension:failure.errorCode deviceCount:[BWIAnalyticsHelper dimensionForDeviceCount: failure.deviceCount] unspecifiedErrorMessage:failure.unspecifiedErrorMessage]; + [BWIAnalytics.sharedTracker trackE2EEErrorWithDimension:failure.errorCode deviceCount:[[BWIAnalyticsHelper shared] dimensionForDeviceCount: failure.deviceCount] unspecifiedErrorMessage:failure.unspecifiedErrorMessage]; } else { failuresCounts[@(failure.reason)] = @(failuresCounts[@(failure.reason)].unsignedIntegerValue + 1); } diff --git a/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/RoomInviteViewController.swift b/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/RoomInviteViewController.swift index 44643f498..7b5524645 100644 --- a/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/RoomInviteViewController.swift +++ b/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/RoomInviteViewController.swift @@ -34,7 +34,7 @@ class RoomInviteViewController: ContactsTableViewController { private func setupShareInviteLinkHeader() { guard roomAlias != nil, - RiotSettings.shared.allowInviteExernalUsers, + BWIBuildSettings.shared.allowInviteExernalUsers, joinRule != .invite, joinRule != .restricted else { contactsTableView.tableHeaderView = nil diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b85f3cd3e..12db9e206 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -8217,7 +8217,7 @@ static CGSize kThreadListBarButtonItemImageSize; if( [profile isLogable] ) { [self.roomDataSource.room members:^(MXRoomMembers *roomMembers) { NSUInteger noOfUsers = roomMembers.joinedMembers.count; - [BWIAnalyticsHelper getRoomDeviceCountWithRoom:self.roomDataSource.room completion:^(NSInteger deviceCount) { + [[BWIAnalyticsHelper shared] getRoomDeviceCountWithRoom:self.roomDataSource.room completion:^(NSInteger deviceCount) { [profile log2AnalyticsWithUsers:noOfUsers devices:deviceCount]; }]; } failure:^(NSError *error) { @@ -8228,8 +8228,8 @@ static CGSize kThreadListBarButtonItemImageSize; // Bwi #4795: voice message - (void) trackVoiceMessage:(NSInteger)duration { - [BWIAnalyticsHelper getRoomDeviceCountWithRoom:self.roomDataSource.room completion:^(NSInteger deviceCount) { - NSString *deviceCountString = [BWIAnalyticsHelper dimensionForDeviceCount: deviceCount]; + [[BWIAnalyticsHelper shared] getRoomDeviceCountWithRoom:self.roomDataSource.room completion:^(NSInteger deviceCount) { + NSString *deviceCountString = [[BWIAnalyticsHelper shared] dimensionForDeviceCount: deviceCount]; NSNumber *durationInSeconds = [NSNumber numberWithInteger:(duration / 1000)]; [BWIAnalytics.sharedTracker trackEventWithDimensionWithCategory:@"Feature" action:@"SendVoiceMessage" dimension:deviceCountString value:durationInSeconds name:@"send_voice_message_default"]; }]; diff --git a/Riot/Modules/StartChat/StartChatViewController.m b/Riot/Modules/StartChat/StartChatViewController.m index 055ff1986..ee669430f 100644 --- a/Riot/Modules/StartChat/StartChatViewController.m +++ b/Riot/Modules/StartChat/StartChatViewController.m @@ -142,7 +142,7 @@ return; } - if (!RiotSettings.shared.allowInviteExernalUsers) + if (!BWIBuildSettings.shared.allowInviteExernalUsers) { self.contactsTableView.tableHeaderView = nil; return; diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 4b726c9e1..d5c427b3a 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -343,7 +343,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; if (mxSession.state == MXSessionStateRunning) { // Track e2e failures dispatch_async(dispatch_get_main_queue(), ^{ - [BWIAnalyticsHelper getRoomDeviceCountWithRoom:[self->mxSession roomWithRoomId:roomState.roomId] completion:^(NSInteger deviceCount) { + [[BWIAnalyticsHelper shared] getRoomDeviceCountWithRoom:[self->mxSession roomWithRoomId:roomState.roomId] completion:^(NSInteger deviceCount) { [[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:self->mxSession.myUser.userId roomDeviceCount:deviceCount]; }]; }); diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index 83df140a9..9b4d7a77c 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -52,6 +52,7 @@ targets: - path: ../bwi/NotificationTimes/Date+fromHourMinute.swift - path: ../bwi/MatomoAnalytics/BWIAnalytics.swift - path: ../bwi/MatomoAnalytics/BWIAnalyticsHelper.swift + - path: ../bwi/MatomoAnalytics/LazyRoomDevices.swift - path: ../bwi/MatomoAnalytics/BWIAnalyticsAccountDataService.swift - path: ../bwi/MatomoAnalytics/AnalyticsConfiguration.swift - path: ../bwi/MatomoAnalytics/DecryptedEvent.swift diff --git a/RiotShareExtension/Shared/ForwardingShareItemSender.swift b/RiotShareExtension/Shared/ForwardingShareItemSender.swift index 5c17a4887..e85a1acc2 100644 --- a/RiotShareExtension/Shared/ForwardingShareItemSender.swift +++ b/RiotShareExtension/Shared/ForwardingShareItemSender.swift @@ -75,9 +75,9 @@ class ForwardingShareItemSender: NSObject, ShareItemSenderProtocol { } func trackForwardMessage(room: MXRoom) { - BWIAnalyticsHelper.getRoomDeviceCount(room: room) { deviceCount in - let deviceCountString = BWIAnalyticsHelper.dimensionForDeviceCount(deviceCount) - let messageType = BWIAnalyticsHelper.getForwardingType(event: self.event) + BWIAnalyticsHelper.shared.getRoomDeviceCount(room: room) { deviceCount in + let deviceCountString = BWIAnalyticsHelper.shared.dimensionForDeviceCount(deviceCount) + let messageType = BWIAnalyticsHelper.shared.getForwardingType(event: self.event) BWIAnalytics.sharedTracker.trackEventWithDimension(category: "Feature", action: "ForwardMessage", dimension: deviceCountString, value: nil, name: messageType) } } diff --git a/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift b/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift index c3f44b40a..8a6c3abc8 100644 --- a/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift @@ -159,7 +159,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable { // MARK: Bwi tracking private func log2Analytics(details: EditFormPollDetails, room: MXRoom) { - BWIAnalyticsHelper.getRoomDeviceCount(room: room) { deviceCount in + BWIAnalyticsHelper.shared.getRoomDeviceCount(room: room) { deviceCount in var eventName: String switch details.type { case .undisclosed: @@ -176,7 +176,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable { } } - BWIAnalytics.sharedTracker.trackEventWithDimension(category: "Feature", action: "SendPoll", dimension: BWIAnalyticsHelper.dimensionForDeviceCount(deviceCount), value: NSNumber(value: details.answerOptions.count), name: eventName) + BWIAnalytics.sharedTracker.trackEventWithDimension(category: "Feature", action: "SendPoll", dimension: BWIAnalyticsHelper.shared.dimensionForDeviceCount(deviceCount), value: NSNumber(value: details.answerOptions.count), name: eventName) } } } diff --git a/bwi/MatomoAnalytics/BWIAnalyticsHelper.swift b/bwi/MatomoAnalytics/BWIAnalyticsHelper.swift index 62f4f7f96..a71269dc7 100644 --- a/bwi/MatomoAnalytics/BWIAnalyticsHelper.swift +++ b/bwi/MatomoAnalytics/BWIAnalyticsHelper.swift @@ -17,24 +17,38 @@ import Foundation import MatrixSDK + @objc class BWIAnalyticsHelper: NSObject { + @objc static let shared = BWIAnalyticsHelper() - @objc static func getRoomDeviceCount(room: MXRoom, completion: @escaping(Int) -> ()) { - room.members { roomMembers in - var noOfDevices = 0 - for member in roomMembers?.joinedMembers ?? [MXRoomMember]() { - noOfDevices += room.mxSession.crypto.devices(forUser: member.userId).count + private let serialQueue = DispatchQueue(label: "de.bwi.analyticshelper.serialQueue") + + @objc func getRoomDeviceCount(room: MXRoom, completion: @escaping(Int) -> ()) { + // only ask for devices per room if a refresh is needed -> room.mxSession.crypto.devices is costly in large rooms. And even with that performance win don't do it on Mainthread + serialQueue.async { + if LazyRoomDevices.shared.areDevicesCurrent(room: room) { + completion(LazyRoomDevices.shared.deviceCount(room: room)) + return + } + + room.members { roomMembers in + var noOfDevices = 0 + for member in roomMembers?.joinedMembers ?? [MXRoomMember]() { + noOfDevices += room.mxSession.crypto.devices(forUser: member.userId).count + } + LazyRoomDevices.shared.setDeviceCount(room: room, deviceCount: noOfDevices) + completion(noOfDevices) + + } lazyLoadedMembers: { _ in + completion(0) + } failure: { error in + MXLog.error("[RoomAnalyticsHelper] Failed loading room", context: error) + completion(0) } - completion(noOfDevices) - } lazyLoadedMembers: { _ in - completion(0) - } failure: { error in - MXLog.error("[RoomAnalyticsHelper] Failed loading room", context: error) - completion(0) } } - @objc static func getForwardingType(event: MXEvent) -> String? { + @objc func getForwardingType(event: MXEvent) -> String? { guard let messageType: MXMessageType = event.messageType else { return nil } @@ -66,7 +80,7 @@ import MatrixSDK // MARK: custom dimensions - @objc static func dimensionForDeviceCount(_ deviceCount: Int) -> String { + @objc func dimensionForDeviceCount(_ deviceCount: Int) -> String { if deviceCount <= 0 { return "Undefiniert" } diff --git a/bwi/MatomoAnalytics/LazyRoomDevices.swift b/bwi/MatomoAnalytics/LazyRoomDevices.swift new file mode 100644 index 000000000..42fbf437d --- /dev/null +++ b/bwi/MatomoAnalytics/LazyRoomDevices.swift @@ -0,0 +1,50 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * 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 + +struct LazyDevice { + let timeStamp: Date + let deviceCount: Int +} +class LazyRoomDevices : NSObject { + static let shared = LazyRoomDevices() + + // reload devices older than 10 minutes + let threshold = 10.0 * 60.0 + + var devices: Dictionary = Dictionary() + + func deviceCount( room: MXRoom ) -> Int { + guard let lazyDevice = devices[room.roomId] else { + return 0 + } + return lazyDevice.deviceCount + } + + func areDevicesCurrent( room: MXRoom) -> Bool { + guard let lazyDevice = devices[room.roomId] else { + return false + } + return Date().timeIntervalSince(lazyDevice.timeStamp) < threshold + + } + + func setDeviceCount( room: MXRoom, deviceCount: Int) { + devices[room.roomId] = LazyDevice(timeStamp: Date(), deviceCount: deviceCount) + } +} diff --git a/bwi/PerformanceProfiles/PerformanceProfile.swift b/bwi/PerformanceProfiles/PerformanceProfile.swift index 98b9b6d86..bcbe1402c 100644 --- a/bwi/PerformanceProfiles/PerformanceProfile.swift +++ b/bwi/PerformanceProfiles/PerformanceProfile.swift @@ -53,7 +53,7 @@ import Foundation func log2Analytics(users: Int, devices: Int) { if isLogable() { - BWIAnalytics.sharedTracker.trackSlowMessage(dimension: BWIAnalyticsHelper.dimensionForDeviceCount(devices), value: Int(timeInterval*1000)) + BWIAnalytics.sharedTracker.trackSlowMessage(dimension: BWIAnalyticsHelper.shared.dimensionForDeviceCount(devices), value: Int(timeInterval*1000)) } } }