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
@@ -44,7 +44,7 @@ struct StartChatScreenViewStateBindings {
/// Information describing the currently displayed alert.
var alertInfo: AlertInfo<StartChatScreenErrorType>?
var selectedUserToInvite: UserProfileProxy?
var selectedUserToInvite: UserToInvite?
var isJoinRoomByAddressSheetPresented = false
}
@@ -67,8 +67,21 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
hideLoadingIndicator()
actionsSubject.send(.showRoom(roomID: roomId))
case .success:
hideLoadingIndicator()
state.bindings.selectedUserToInvite = user
if appSettings.enableKeyShareOnInvite {
Task {
// If an error occured while fetching the identity, assume they are unknown.
let isUnknown = if case .success(let identity) = await self.userSession.clientProxy.userIdentity(for: user.userID, fallBackToServer: false) {
identity == nil
} else {
true
}
self.state.bindings.selectedUserToInvite = UserToInvite(user: user, isUnknown: isUnknown)
hideLoadingIndicator()
}
} else {
hideLoadingIndicator()
state.bindings.selectedUserToInvite = UserToInvite(user: user, isUnknown: false)
}
case .failure:
hideLoadingIndicator()
displayError()
@@ -10,7 +10,7 @@ import Compound
import SwiftUI
struct SendInviteConfirmationView: View {
let userToInvite: UserProfileProxy
let userToInvite: UserToInvite
let mediaProvider: MediaProviderProtocol?
let onInvite: () -> Void
@@ -19,14 +19,26 @@ struct SendInviteConfirmationView: View {
@State private var sheetHeight: CGFloat = .zero
private let topPadding: CGFloat = 24
private var title: String {
if userToInvite.isUnknown {
L10n.screenBottomSheetCreateDmUnknownUserTitle
} else {
L10n.screenBottomSheetCreateDmTitle
}
}
private var subtitle: String {
let string: String
if let displayName = userToInvite.displayName {
string = L10n.commonNameAndId(displayName, userToInvite.userID)
string = L10n.commonNameAndId(displayName, userToInvite.id)
} else {
string = userToInvite.userID
string = userToInvite.id
}
return if userToInvite.isUnknown {
L10n.screenBottomSheetCreateDmUnknownUserContent
} else {
L10n.screenBottomSheetCreateDmMessage(string)
}
return L10n.screenBottomSheetCreateDmMessage(string)
}
var body: some View {
@@ -48,11 +60,11 @@ struct SendInviteConfirmationView: View {
VStack(spacing: 16) {
LoadableAvatarImage(url: userToInvite.avatarURL,
name: userToInvite.displayName,
contentID: userToInvite.userID,
contentID: userToInvite.id,
avatarSize: .user(on: .sendInviteConfirmation),
mediaProvider: mediaProvider)
VStack(spacing: 8) {
Text(L10n.screenBottomSheetCreateDmTitle)
Text(title)
.multilineTextAlignment(.center)
.font(.compound.headingMDBold)
.foregroundStyle(.compound.textPrimary)
@@ -91,7 +103,22 @@ struct SendInviteConfirmationView: View {
struct SendInviteConfirmationView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
SendInviteConfirmationView(userToInvite: .mockBob,
SendInviteConfirmationView(userToInvite: .mockKnownBob,
mediaProvider: nil) { }
.previewDisplayName("With Known Identity")
SendInviteConfirmationView(userToInvite: .mockUnknownBob,
mediaProvider: nil) { }
.previewDisplayName("With Unknown Identity")
}
}
private extension UserToInvite {
static var mockKnownBob: Self {
.init(user: .mockBob, isUnknown: false)
}
static var mockUnknownBob: Self {
.init(user: .mockBob, isUnknown: true)
}
}
@@ -32,9 +32,10 @@ struct StartChatScreen: View {
disablesInteractiveDismiss: true)
.compoundSearchField()
.alert(item: $context.alertInfo)
.sheet(item: $context.selectedUserToInvite) { user in
SendInviteConfirmationView(userToInvite: user, mediaProvider: context.mediaProvider) {
context.send(viewAction: .createDM(user: user))
.sheet(item: $context.selectedUserToInvite) { userToInvite in
SendInviteConfirmationView(userToInvite: userToInvite,
mediaProvider: context.mediaProvider) {
context.send(viewAction: .createDM(user: userToInvite.user))
}
}
.sheet(isPresented: $context.isJoinRoomByAddressSheetPresented) {