Release 2.0.0

This commit is contained in:
Frank Rotermund
2022-11-27 13:18:53 +00:00
parent bf57719009
commit 0dc8ec0982
570 changed files with 20366 additions and 4410 deletions
@@ -44,7 +44,23 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol {
session.crypto.device(withDeviceId: deviceId, ofUser: userId)
}
func verificationState(for deviceInfo: MXDeviceInfo?) -> UserSessionInfo.VerificationState {
guard let deviceInfo = deviceInfo else { return .unknown }
guard session.crypto?.crossSigning.canCrossSign == true else {
return deviceInfo.deviceId == session.myDeviceId ? .unverified : .unknown
}
return deviceInfo.trustLevel.isVerified ? .verified : .unverified
}
func accountData(for eventType: String) -> [AnyHashable: Any]? {
session.accountData.accountData(forEventType: eventType)
}
func qrLoginAvailable() async throws -> Bool {
let service = QRLoginService(client: session.matrixRestClient,
mode: .authenticated)
return try await service.isServiceAvailable()
}
}
@@ -28,5 +28,9 @@ protocol UserSessionsDataProviderProtocol {
func device(withDeviceId deviceId: String, ofUser userId: String) -> MXDeviceInfo?
func verificationState(for deviceInfo: MXDeviceInfo?) -> UserSessionInfo.VerificationState
func accountData(for eventType: String) -> [AnyHashable: Any]?
func qrLoginAvailable() async throws -> Bool
}
@@ -14,7 +14,7 @@
// limitations under the License.
//
import Foundation
import Combine
import MatrixSDK
class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
@@ -22,18 +22,22 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
private static let inactiveSessionDurationTreshold: TimeInterval = 90 * 86400
private let dataProvider: UserSessionsDataProviderProtocol
private var cancellables: Set<AnyCancellable> = []
private(set) var overviewData: UserSessionsOverviewData
private(set) var overviewDataPublisher: CurrentValueSubject<UserSessionsOverviewData, Never>
private(set) var sessionInfos: [UserSessionInfo]
init(dataProvider: UserSessionsDataProviderProtocol) {
self.dataProvider = dataProvider
overviewData = UserSessionsOverviewData(currentSession: nil,
unverifiedSessions: [],
inactiveSessions: [],
otherSessions: [])
overviewDataPublisher = .init(UserSessionsOverviewData(currentSession: nil,
unverifiedSessions: [],
inactiveSessions: [],
otherSessions: [],
linkDeviceEnabled: false))
sessionInfos = []
setupInitialOverviewData()
listenForSessionUpdates()
}
// MARK: - Public
@@ -42,8 +46,14 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
dataProvider.devices { response in
switch response {
case .success(let devices):
self.overviewData = self.sessionsOverviewData(from: devices)
completion(.success(self.overviewData))
self.sessionInfos = self.sortedSessionInfos(from: devices)
Task { @MainActor in
let linkDeviceEnabled = try? await self.dataProvider.qrLoginAvailable()
let overviewData = self.sessionsOverviewData(from: self.sessionInfos,
linkDeviceEnabled: linkDeviceEnabled ?? false)
self.overviewDataPublisher.send(overviewData)
completion(.success(overviewData))
}
case .failure(let error):
completion(.failure(error))
}
@@ -51,24 +61,43 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
}
func sessionForIdentifier(_ sessionId: String) -> UserSessionInfo? {
if overviewData.currentSession?.id == sessionId {
return overviewData.currentSession
if currentSession?.id == sessionId {
return currentSession
}
return overviewData.otherSessions.first(where: { $0.id == sessionId })
return otherSessions.first(where: { $0.id == sessionId })
}
// MARK: - Private
private func listenForSessionUpdates() {
NotificationCenter.default.publisher(for: .MXDeviceInfoTrustLevelDidChange)
.sink { [weak self] _ in
self?.updateOverviewData { _ in }
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: .MXDeviceListDidUpdateUsersDevices)
.sink { [weak self] _ in
self?.updateOverviewData { _ in }
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: .MXCrossSigningInfoTrustLevelDidChange)
.sink { [weak self] _ in
self?.updateOverviewData { _ in }
}
.store(in: &cancellables)
}
private func setupInitialOverviewData() {
guard let currentSessionInfo = getCurrentSessionInfo() else {
return
}
overviewData = UserSessionsOverviewData(currentSession: currentSessionInfo,
unverifiedSessions: currentSessionInfo.isVerified ? [] : [currentSessionInfo],
inactiveSessions: currentSessionInfo.isActive ? [] : [currentSessionInfo],
otherSessions: [])
overviewDataPublisher = .init(UserSessionsOverviewData(currentSession: currentSessionInfo,
unverifiedSessions: currentSessionInfo.verificationState == .verified ? [] : [currentSessionInfo],
inactiveSessions: currentSessionInfo.isActive ? [] : [currentSessionInfo],
otherSessions: [],
linkDeviceEnabled: false))
}
private func getCurrentSessionInfo() -> UserSessionInfo? {
@@ -78,20 +107,25 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
}
return sessionInfo(from: device, isCurrentSession: true)
}
private func sessionsOverviewData(from devices: [MXDevice]) -> UserSessionsOverviewData {
let allSessions = devices
private func sortedSessionInfos(from devices: [MXDevice]) -> [UserSessionInfo] {
devices
.sorted { $0.lastSeenTs > $1.lastSeenTs }
.map { sessionInfo(from: $0, isCurrentSession: $0.deviceId == dataProvider.myDeviceId) }
return UserSessionsOverviewData(currentSession: allSessions.filter(\.isCurrent).first,
unverifiedSessions: allSessions.filter { !$0.isVerified },
inactiveSessions: allSessions.filter { !$0.isActive },
otherSessions: allSessions.filter { !$0.isCurrent })
}
private func sessionsOverviewData(from allSessions: [UserSessionInfo],
linkDeviceEnabled: Bool) -> UserSessionsOverviewData {
UserSessionsOverviewData(currentSession: allSessions.filter(\.isCurrent).first,
unverifiedSessions: allSessions.filter { $0.verificationState == .unverified && !$0.isCurrent },
inactiveSessions: allSessions.filter { !$0.isActive },
otherSessions: allSessions.filter { !$0.isCurrent },
linkDeviceEnabled: linkDeviceEnabled)
}
private func sessionInfo(from device: MXDevice, isCurrentSession: Bool) -> UserSessionInfo {
let isSessionVerified = deviceInfo(for: device.deviceId)?.trustLevel.isVerified ?? false
let deviceInfo = deviceInfo(for: device.deviceId)
let verificationState = dataProvider.verificationState(for: deviceInfo)
let eventType = kMXAccountDataTypeClientInformation + "." + device.deviceId
let appData = dataProvider.accountData(for: eventType)
@@ -110,7 +144,7 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
return UserSessionInfo(withDevice: device,
applicationData: appData as? [String: String],
userAgent: userAgent,
isSessionVerified: isSessionVerified,
verificationState: verificationState,
isActive: isSessionActive,
isCurrent: isCurrentSession)
}
@@ -128,13 +162,13 @@ extension UserSessionInfo {
init(withDevice device: MXDevice,
applicationData: [String: String]?,
userAgent: UserAgent?,
isSessionVerified: Bool,
verificationState: VerificationState,
isActive: Bool,
isCurrent: Bool) {
self.init(id: device.deviceId,
name: device.displayName,
deviceType: userAgent?.deviceType ?? .unknown,
isVerified: isSessionVerified,
verificationState: verificationState,
lastSeenIP: device.lastSeenIp,
lastSeenTimestamp: device.lastSeenTs > 0 ? TimeInterval(device.lastSeenTs / 1000) : nil,
applicationName: applicationData?["name"],