mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Fixes element-hq/element-meta/issues/2525 - Display a warning when a user's pinned identity changes
This commit is contained in:
@@ -895,6 +895,22 @@ class ClientProxy: ClientProxyProtocol {
|
||||
await client.encryption().curve25519Key()
|
||||
}
|
||||
|
||||
func pinUserIdentity(_ userID: String) async -> Result<Void, ClientProxyError> {
|
||||
MXLog.info("Pinning current identity for user: \(userID)")
|
||||
|
||||
do {
|
||||
guard let userIdentity = try await client.encryption().getUserIdentity(userId: userID) else {
|
||||
MXLog.error("Failed retrieving identity for user: \(userID)")
|
||||
return .failure(.failedRetrievingUserIdentity)
|
||||
}
|
||||
|
||||
return try await .success(userIdentity.pin())
|
||||
} catch {
|
||||
MXLog.error("Failed pinning current identity for user: \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
|
||||
func resetIdentity() async -> Result<IdentityResetHandle?, ClientProxyError> {
|
||||
do {
|
||||
return try await .success(client.encryption().resetIdentity())
|
||||
|
||||
@@ -35,6 +35,7 @@ enum ClientProxyError: Error {
|
||||
case invalidServerName
|
||||
case failedUploadingMedia(Error, MatrixErrorCode)
|
||||
case roomPreviewIsPrivate
|
||||
case failedRetrievingUserIdentity
|
||||
}
|
||||
|
||||
enum SlidingSyncConstants {
|
||||
@@ -196,5 +197,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||
func ed25519Base64() async -> String?
|
||||
func curve25519Base64() async -> String?
|
||||
|
||||
func pinUserIdentity(_ userID: String) async -> Result<Void, ClientProxyError>
|
||||
func resetIdentity() async -> Result<IdentityResetHandle?, ClientProxyError>
|
||||
}
|
||||
|
||||
@@ -295,10 +295,12 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
|
||||
roomProxy
|
||||
.actionsPublisher
|
||||
.map { action -> (Bool, [String]) in
|
||||
.compactMap { action -> (Bool, [String])? in
|
||||
switch action {
|
||||
case .roomInfoUpdate:
|
||||
return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
.removeDuplicates { $0 == $1 }
|
||||
|
||||
@@ -58,6 +58,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
private var roomInfoObservationToken: TaskHandle?
|
||||
// periphery:ignore - required for instance retention in the rust codebase
|
||||
private var typingNotificationObservationToken: TaskHandle?
|
||||
// periphery:ignore - required for instance retention in the rust codebase
|
||||
private var identityStatusChangesObservationToken: TaskHandle?
|
||||
|
||||
private var subscribedForUpdates = false
|
||||
|
||||
@@ -70,6 +72,11 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
var typingMembersPublisher: CurrentValuePublisher<[String], Never> {
|
||||
typingMembersSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let identityStatusChangesSubject = CurrentValueSubject<[IdentityStatusChange], Never>([])
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> {
|
||||
identityStatusChangesSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> {
|
||||
@@ -193,6 +200,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
|
||||
subscribeToRoomInfoUpdates()
|
||||
|
||||
subscribeToIdentityStatusChanges()
|
||||
|
||||
subscribeToTypingNotifications()
|
||||
}
|
||||
|
||||
@@ -708,6 +717,16 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
typingMembersSubject.send(typingMembers)
|
||||
})
|
||||
}
|
||||
|
||||
private func subscribeToIdentityStatusChanges() {
|
||||
identityStatusChangesObservationToken = room.subscribeToIdentityStatusChanges(listener: RoomIdentityStatusChangeListener { [weak self] changes in
|
||||
guard let self else { return }
|
||||
|
||||
MXLog.info("Received identity status changes: \(changes)")
|
||||
|
||||
identityStatusChangesSubject.send(changes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomInfoUpdateListener: RoomInfoListener {
|
||||
@@ -733,3 +752,15 @@ private final class RoomTypingNotificationUpdateListener: TypingNotificationsLis
|
||||
onUpdateClosure(typingUserIds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class RoomIdentityStatusChangeListener: IdentityStatusChangeListener {
|
||||
private let onUpdateClosure: ([IdentityStatusChange]) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping ([IdentityStatusChange]) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func call(identityStatusChange: [IdentityStatusChange]) {
|
||||
onUpdateClosure(identityStatusChange)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,24 @@ final class RoomMemberProxy: RoomMemberProxyProtocol {
|
||||
}
|
||||
|
||||
var userID: String { member.userId }
|
||||
|
||||
var displayName: String? { member.displayName }
|
||||
|
||||
var disambiguatedDisplayName: String? {
|
||||
guard let displayName else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return member.isNameAmbiguous ? "\(displayName) (\(userID))" : displayName
|
||||
}
|
||||
|
||||
var avatarURL: URL? { member.avatarUrl.flatMap(URL.init(string:)) }
|
||||
|
||||
var membership: MembershipState { member.membership }
|
||||
|
||||
var isIgnored: Bool { member.isIgnored }
|
||||
|
||||
var powerLevel: Int { Int(member.powerLevel) }
|
||||
|
||||
var role: RoomMemberRole { member.suggestedRoleForPowerLevel }
|
||||
}
|
||||
|
||||
@@ -11,13 +11,18 @@ import MatrixRustSDK
|
||||
// sourcery: AutoMockable
|
||||
protocol RoomMemberProxyProtocol: AnyObject {
|
||||
var userID: String { get }
|
||||
|
||||
var displayName: String? { get }
|
||||
var disambiguatedDisplayName: String? { get }
|
||||
|
||||
var avatarURL: URL? { get }
|
||||
|
||||
var membership: MembershipState { get }
|
||||
|
||||
var isIgnored: Bool { get }
|
||||
|
||||
var powerLevel: Int { get }
|
||||
|
||||
var role: RoomMemberRole { get }
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ protocol InvitedRoomProxyProtocol: RoomProxyProtocol {
|
||||
func acceptInvitation() async -> Result<Void, RoomProxyError>
|
||||
}
|
||||
|
||||
enum JoinedRoomProxyAction {
|
||||
enum JoinedRoomProxyAction: Equatable {
|
||||
case roomInfoUpdate
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
|
||||
var typingMembersPublisher: CurrentValuePublisher<[String], Never> { get }
|
||||
|
||||
var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { get }
|
||||
|
||||
var actionsPublisher: AnyPublisher<JoinedRoomProxyAction, Never> { get }
|
||||
|
||||
var timeline: TimelineProxyProtocol { get }
|
||||
|
||||
@@ -19,7 +19,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
case unknownDevice(color: Color)
|
||||
case unsignedDevice(color: Color)
|
||||
case unverifiedIdentity(color: Color)
|
||||
case previouslyVerified(color: Color)
|
||||
case verificationViolation(color: Color)
|
||||
case sentInClear(color: Color)
|
||||
|
||||
var message: String {
|
||||
@@ -32,7 +32,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
L10n.eventShieldReasonUnsignedDevice
|
||||
case .unverifiedIdentity:
|
||||
L10n.eventShieldReasonUnverifiedIdentity
|
||||
case .previouslyVerified:
|
||||
case .verificationViolation:
|
||||
L10n.eventShieldReasonPreviouslyVerified
|
||||
case .sentInClear:
|
||||
L10n.eventShieldReasonSentInClear
|
||||
@@ -45,7 +45,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
.unknownDevice(let color),
|
||||
.unsignedDevice(let color),
|
||||
.unverifiedIdentity(let color),
|
||||
.previouslyVerified(let color),
|
||||
.verificationViolation(let color),
|
||||
.sentInClear(let color):
|
||||
color
|
||||
}
|
||||
@@ -54,7 +54,7 @@ enum EncryptionAuthenticity: Hashable {
|
||||
var icon: KeyPath<CompoundIcons, Image> {
|
||||
switch self {
|
||||
case .notGuaranteed: \.info
|
||||
case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .previouslyVerified: \.helpSolid
|
||||
case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .verificationViolation: \.helpSolid
|
||||
case .sentInClear: \.lockOff
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,8 @@ extension EncryptionAuthenticity {
|
||||
self = .unsignedDevice(color: color)
|
||||
case .unverifiedIdentity:
|
||||
self = .unverifiedIdentity(color: color)
|
||||
case .previouslyVerified:
|
||||
self = .previouslyVerified(color: color)
|
||||
case .verificationViolation:
|
||||
self = .verificationViolation(color: color)
|
||||
case .sentInClear:
|
||||
self = .sentInClear(color: color)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user