core: fix retrying connection via contact card (preset contacts) (#6104)

* core: fix retrying connection via contact card (preset contacts)

* ios

* kotlin

* banner
This commit is contained in:
spaced4ndy
2025-07-25 16:31:05 +00:00
committed by GitHub
parent 749f6adc00
commit e9a3eb8f10
9 changed files with 95 additions and 51 deletions

View File

@@ -208,8 +208,14 @@ struct ContactListNavLink: View {
.tint(.red)
}
.confirmationDialog("Connect with \(contact.chatViewName)", isPresented: $showConnectContactViaAddressDialog, titleVisibility: .visible) {
Button("Use current profile") { connectContactViaAddress_(contact, false) }
Button("Use new incognito profile") { connectContactViaAddress_(contact, true) }
if !contact.profileChangeProhibited {
Button("Use current profile") { connectContactViaAddress_(contact, false) }
Button("Use new incognito profile") { connectContactViaAddress_(contact, true) }
} else if !contact.contactConnIncognito {
Button("Use current profile") { connectContactViaAddress_(contact, false) }
} else {
Button("Use incognito profile") { connectContactViaAddress_(contact, true) }
}
}
}

View File

@@ -178,8 +178,8 @@
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a */; };
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
@@ -543,8 +543,8 @@
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a"; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a"; sourceTree = "<group>"; };
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
@@ -704,8 +704,8 @@
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -790,8 +790,8 @@
64C829992D54AEEE006B9E89 /* libffi.a */,
64C829982D54AEED006B9E89 /* libgmp.a */,
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.7-HtMnGcLT3joL6B8buibOdF.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.1.0-CLkstteZ5zsL7AgM2nJh38.a */,
);
path = Libraries;
sourceTree = "<group>";

View File

@@ -1793,7 +1793,7 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
}
public var isContactCard: Bool {
activeConn == nil && profile.contactLink != nil && active && preparedContact == nil && contactRequestId == nil
(activeConn == nil || activeConn?.connStatus == .prepared) && profile.contactLink != nil && active && preparedContact == nil && contactRequestId == nil
}
public var contactConnIncognito: Bool {

View File

@@ -1778,7 +1778,7 @@ data class Contact(
}
val isContactCard: Boolean =
activeConn == null && profile.contactLink != null && active && preparedContact == null && contactRequestId == null
(activeConn == null || activeConn.connStatus == ConnStatus.Prepared) && profile.contactLink != null && active && preparedContact == null && contactRequestId == null
val contactConnIncognito =
activeConn?.customUserProfileId != null

View File

@@ -794,33 +794,49 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress(
close: (() -> Unit)?,
openChat: Boolean
) {
@Composable
fun UseCurrentProfileButton() {
SectionItemView({
AlertManager.privacySensitive.hideAlert()
withBGApi {
val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = false)
if (ok && openChat) {
close?.invoke()
openDirectChat(rhId, contact.contactId)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
@Composable
fun UseIncognitoProfileButton(text: String) {
SectionItemView({
AlertManager.privacySensitive.hideAlert()
withBGApi {
val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = true)
if (ok && openChat) {
close?.invoke()
openDirectChat(rhId, contact.contactId)
}
}
}) {
Text(text, Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
AlertManager.privacySensitive.showAlertDialogButtonsColumn(
title = String.format(generalGetString(MR.strings.connect_with_contact_name_question), contact.chatViewName),
buttons = {
Column {
SectionItemView({
AlertManager.privacySensitive.hideAlert()
withBGApi {
close?.invoke()
val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = false)
if (ok && openChat) {
openDirectChat(rhId, contact.contactId)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.privacySensitive.hideAlert()
withBGApi {
close?.invoke()
val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = true)
if (ok && openChat) {
openDirectChat(rhId, contact.contactId)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
if (!contact.profileChangeProhibited) {
UseCurrentProfileButton()
UseIncognitoProfileButton(generalGetString(MR.strings.connect_use_new_incognito_profile))
} else if (!contact.contactConnIncognito) {
UseCurrentProfileButton()
} else {
UseIncognitoProfileButton(generalGetString(MR.strings.connect_use_incognito_profile))
}
SectionItemView({
AlertManager.privacySensitive.hideAlert()

View File

@@ -104,6 +104,9 @@ fun ContactPreviewView(
modifier = Modifier
.size(21.dp)
)
if (chat.chatInfo.incognito) {
Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON))
}
}
if (showDeletedChatIcon && chat.chatInfo.chatDeleted) {

View File

@@ -9,6 +9,7 @@
<string name="connect_via_group_link">Join group?</string>
<string name="connect_use_current_profile">Use current profile</string>
<string name="connect_use_new_incognito_profile">Use new incognito profile</string>
<string name="connect_use_incognito_profile">Use incognito profile</string>
<string name="profile_will_be_sent_to_contact_sending_link">Your profile will be sent to the contact that you received this link from.</string>
<string name="you_will_join_group">You will connect to all group members.</string>
<string name="connect_via_link_verb">Connect</string>

View File

@@ -1909,14 +1909,18 @@ processChatCommand vr nm = \case
Connect _ Nothing -> throwChatError CEInvalidConnReq
APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do
ct@Contact {activeConn, profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db vr user contactId
when (isJust activeConn) $ throwCmdError "contact already has connection"
ccLink <- case contactLink of
Just (CLFull cReq) -> pure $ CCLink cReq Nothing
Just (CLShort sLnk) -> do
(cReq, _cData) <- getShortLinkConnReq user sLnk
pure $ CCLink cReq $ Just sLnk
Nothing -> throwCmdError "no address in contact profile"
connectContactViaAddress user incognito ct ccLink
connectContactViaAddress user incognito ct ccLink `catchChatError` \e -> do
-- get updated contact, in case connection was started - in UI it would lock ability to change incognito choice
-- on next connection attempt, in case server received request while client got network error
ct' <- withFastStore $ \db -> getContact db vr user contactId
toView $ CEvtChatInfoUpdated user (AChatInfo SCTDirect $ DirectChat ct')
throwError e
ConnectSimplex incognito -> withUser $ \user -> do
plan <- contactRequestPlan user adminContactReq Nothing `catchChatError` const (pure $ CPContactAddress (CAPOk Nothing))
connectWithPlan user incognito (ACCL SCMContact (CCLink adminContactReq Nothing)) plan
@@ -3021,7 +3025,6 @@ processChatCommand vr nm = \case
let incognitoProfile = fromLocalProfile <$> localIncognitoProfile
conn' <- joinContact user conn cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup PQSupportOn
pure $ CVRSentInvitation conn' incognitoProfile
mkXContactId = maybe (XContactId <$> drgRandomBytes 16) pure
connect' groupLinkId cReqHash xContactId_ = do
let inGroup = isJust groupLinkId
pqSup = if inGroup then PQSupportOff else PQSupportOn
@@ -3035,19 +3038,31 @@ processChatCommand vr nm = \case
conn' <- joinContact user conn cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup pqSup
pure $ CVRSentInvitation conn' incognitoProfile
connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> CreatedLinkContact -> CM ChatResponse
connectContactViaAddress user@User {userId} incognito ct@Contact {contactId} (CCLink cReq shortLink) =
withInvitationLock "connectContactViaAddress" (strEncode cReq) $ do
newXContactId <- XContactId <$> drgRandomBytes 16
let pqSup = PQSupportOn
(connId, chatV) <- prepareContact user cReq pqSup
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
subMode <- chatReadVar subscriptionMode
conn <- withFastStore' $ \db -> createConnReqConnection db userId connId (Just $ PCEContact ct) cReqHash shortLink newXContactId incognitoProfile Nothing subMode chatV pqSup
void $ joinContact user conn cReq incognitoProfile newXContactId Nothing Nothing False pqSup
ct' <- withStore $ \db -> getContact db vr user contactId
pure $ CRSentInvitationToContact user ct' incognitoProfile
connectContactViaAddress user@User {userId} incognito ct@Contact {contactId, activeConn} (CCLink cReq shortLink) =
withInvitationLock "connectContactViaAddress" (strEncode cReq) $
case activeConn of
Nothing -> do
let pqSup = PQSupportOn
(connId, chatV) <- prepareContact user cReq pqSup
newXContactId <- XContactId <$> drgRandomBytes 16
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
subMode <- chatReadVar subscriptionMode
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
conn <- withFastStore' $ \db -> createConnReqConnection db userId connId (Just $ PCEContact ct) cReqHash shortLink newXContactId incognitoProfile Nothing subMode chatV pqSup
void $ joinContact user conn cReq incognitoProfile newXContactId Nothing Nothing False pqSup
ct' <- withStore $ \db -> getContact db vr user contactId
pure $ CRSentInvitationToContact user ct' incognitoProfile
Just conn@Connection {connStatus, xContactId = xContactId_, customUserProfileId} -> case connStatus of
ConnPrepared -> do
when (incognito /= isJust customUserProfileId) $ throwCmdError "incognito mode is different from prepared connection"
xContactId <- mkXContactId xContactId_
localIncognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
let incognitoProfile = fromLocalProfile <$> localIncognitoProfile
void $ joinContact user conn cReq incognitoProfile xContactId Nothing Nothing False PQSupportOn
ct' <- withStore $ \db -> getContact db vr user contactId
pure $ CRSentInvitationToContact user ct' incognitoProfile
_ -> throwCmdError "contact already has connection"
prepareContact :: User -> ConnReqContact -> PQSupport -> CM (ConnId, VersionChat)
prepareContact user cReq pqSup = do
-- 0) toggle disabled - PQSupportOff
@@ -3059,6 +3074,8 @@ processChatCommand vr nm = \case
let chatV = agentToChatVersion agentV
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup
pure (connId, chatV)
mkXContactId :: Maybe XContactId -> CM XContactId
mkXContactId = maybe (XContactId <$> drgRandomBytes 16) pure
joinContact :: User -> Connection -> ConnReqContact -> Maybe Profile -> XContactId -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> Bool -> PQSupport -> CM Connection
joinContact user conn@Connection {connChatVersion = chatV} cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup pqSup = do
let profileToSend =

View File

@@ -546,6 +546,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
(gInfo, host) <- withStore $ \db -> do
liftIO $ deleteContactCardKeepConn db connId ct
createGroupInvitedViaLink db vr user conn'' glInv
void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing (Just epochStart)
-- [incognito] send saved profile
incognitoProfile <- forM customUserProfileId $ \pId -> withStore (\db -> getProfileById db userId pId)
let profileToSend = userProfileInGroup user (fromLocalProfile <$> incognitoProfile)