Prompt user when inviting users with uncached identities (#5331)

If the user attempts to invite someone (to a room or creating a DM) whose identity is not cached, we prompt them to make sure this was their intention.
This commit is contained in:
Skye Elliot
2026-04-14 12:51:58 +01:00
committed by GitHub
parent fe2319cada
commit 78240ebdee
45 changed files with 541 additions and 40 deletions
@@ -15,6 +15,7 @@ struct UserProfileScreenCoordinatorParameters {
let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService
let appSettings: AppSettings
}
enum UserProfileScreenCoordinatorAction {
@@ -38,7 +39,8 @@ final class UserProfileScreenCoordinator: CoordinatorProtocol {
isPresentedModally: parameters.isPresentedModally,
userSession: parameters.userSession,
userIndicatorController: parameters.userIndicatorController,
analytics: parameters.analytics)
analytics: parameters.analytics,
appSettings: parameters.appSettings)
}
func start() {
@@ -20,6 +20,7 @@ struct UserProfileScreenViewState: BindableState {
let isPresentedModally: Bool
var userProfile: UserProfileProxy?
var isIdentityKnown = false
var isVerified: Bool?
var permalink: URL?
var dmRoomID: String?
@@ -33,7 +34,7 @@ struct UserProfileScreenViewState: BindableState {
struct UserProfileScreenViewStateBindings {
var alertInfo: AlertInfo<UserProfileScreenAlertType>?
var inviteConfirmationUser: UserProfileProxy?
var inviteConfirmationUser: UserToInvite?
/// A media item that will be previewed with QuickLook.
var mediaPreviewItem: MediaPreviewItem?
@@ -16,6 +16,7 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
private let userSession: UserSessionProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService
private let appSettings: AppSettings
private var actionsSubject: PassthroughSubject<UserProfileScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<UserProfileScreenViewModelAction, Never> {
@@ -26,10 +27,12 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
isPresentedModally: Bool,
userSession: UserSessionProtocol,
userIndicatorController: UserIndicatorControllerProtocol,
analytics: AnalyticsService) {
analytics: AnalyticsService,
appSettings: AppSettings) {
self.userSession = userSession
self.userIndicatorController = userIndicatorController
self.analytics = analytics
self.appSettings = appSettings
let initialViewState = UserProfileScreenViewState(userID: userID,
isOwnUser: userID == userSession.clientProxy.userID,
@@ -92,8 +95,10 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
}
if case let .success(.some(identity)) = await identityResult {
state.isIdentityKnown = true
state.isVerified = identity.verificationState == .verified
} else {
state.isIdentityKnown = false
MXLog.error("Failed to find the user's identity.")
}
}
@@ -122,7 +127,18 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
if let roomID {
actionsSubject.send(.openDirectChat(roomID: roomID))
} else {
state.bindings.inviteConfirmationUser = userProfile
if appSettings.enableKeyShareOnInvite {
Task {
let isUnknown = if case let .success(identity) = await userSession.clientProxy.userIdentity(for: userProfile.userID, fallBackToServer: false) {
identity == nil
} else {
true
}
state.bindings.inviteConfirmationUser = .init(user: userProfile, isUnknown: isUnknown)
}
} else {
state.bindings.inviteConfirmationUser = .init(user: userProfile, isUnknown: false)
}
}
case .failure:
state.bindings.alertInfo = .init(id: .failedOpeningDirectChat)
@@ -21,8 +21,8 @@ struct UserProfileScreen: View {
.navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar }
.alert(item: $context.alertInfo)
.sheet(item: $context.inviteConfirmationUser) { user in
SendInviteConfirmationView(userToInvite: user,
.sheet(item: $context.inviteConfirmationUser) { userToInvite in
SendInviteConfirmationView(userToInvite: userToInvite,
mediaProvider: context.mediaProvider) {
context.send(viewAction: .createDirectChat)
}
@@ -147,6 +147,7 @@ struct UserProfileScreen_Previews: PreviewProvider, TestablePreview {
isPresentedModally: false,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
userIndicatorController: ServiceLocator.shared.userIndicatorController,
analytics: ServiceLocator.shared.analytics)
analytics: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings)
}
}