mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 09:54:22 +00:00
core: allow to accept contact requests after address is deleted (#6032)
* core: allow to accept contact requests after address is deleted * update * update * plans * ios, postgres migration * schema * request lock, refactor * update simplexmq --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
@@ -996,7 +996,11 @@ func receivedMsgNtf(_ res: NSEChatEvent) async -> (String, NSENotificationData)?
|
||||
// case let .contactConnecting(contact):
|
||||
// TODO profile update
|
||||
case let .receivedContactRequest(user, contactRequest):
|
||||
return (UserContact(contactRequest: contactRequest).id, .contactRequest(user, contactRequest))
|
||||
if let userContactLinkId = contactRequest.userContactLinkId_ {
|
||||
return (UserContact(userContactLinkId: userContactLinkId).id, .contactRequest(user, contactRequest))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .newChatItems(user, chatItems):
|
||||
// Received items are created one at a time
|
||||
if let chatItem = chatItems.first {
|
||||
|
||||
@@ -1908,10 +1908,6 @@ public struct UserContact: Decodable, Hashable {
|
||||
self.userContactLinkId = userContactLinkId
|
||||
}
|
||||
|
||||
public init(contactRequest: UserContactRequest) {
|
||||
self.userContactLinkId = contactRequest.userContactLinkId
|
||||
}
|
||||
|
||||
public var id: String {
|
||||
"@>\(userContactLinkId)"
|
||||
}
|
||||
@@ -1919,7 +1915,7 @@ public struct UserContact: Decodable, Hashable {
|
||||
|
||||
public struct UserContactRequest: Decodable, NamedChat, Hashable {
|
||||
var contactRequestId: Int64
|
||||
public var userContactLinkId: Int64
|
||||
public var userContactLinkId_: Int64?
|
||||
public var cReqChatVRange: VersionRange
|
||||
var localDisplayName: ContactName
|
||||
var profile: Profile
|
||||
@@ -1936,7 +1932,7 @@ public struct UserContactRequest: Decodable, NamedChat, Hashable {
|
||||
|
||||
public static let sampleData = UserContactRequest(
|
||||
contactRequestId: 1,
|
||||
userContactLinkId: 1,
|
||||
userContactLinkId_: 1,
|
||||
cReqChatVRange: VersionRange(1, 1),
|
||||
localDisplayName: "alice",
|
||||
profile: Profile.sampleData,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: a46edd60f0e2460ec4a7d6fb371412f32e988357
|
||||
tag: c5eb66038bb9268671a353638606f6d0bd1de761
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."a46edd60f0e2460ec4a7d6fb371412f32e988357" = "0vix4s9nfxrwiy56rc294s9ik7l9rar4zg2pvl1c4v914lsphybq";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."c5eb66038bb9268671a353638606f6d0bd1de761" = "08b1lqwpmcn139c09lr71gmh5kgxlcdfhvwlnxxafr26m4nc003w";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
@@ -108,6 +108,7 @@ library
|
||||
Simplex.Chat.Store.Postgres.Migrations.M20250512_member_admission
|
||||
Simplex.Chat.Store.Postgres.Migrations.M20250513_group_scope
|
||||
Simplex.Chat.Store.Postgres.Migrations.M20250526_short_links
|
||||
Simplex.Chat.Store.Postgres.Migrations.M20250702_contact_requests_remove_cascade_delete
|
||||
else
|
||||
exposed-modules:
|
||||
Simplex.Chat.Archive
|
||||
@@ -241,6 +242,7 @@ library
|
||||
Simplex.Chat.Store.SQLite.Migrations.M20250512_member_admission
|
||||
Simplex.Chat.Store.SQLite.Migrations.M20250513_group_scope
|
||||
Simplex.Chat.Store.SQLite.Migrations.M20250526_short_links
|
||||
Simplex.Chat.Store.SQLite.Migrations.M20250702_contact_requests_remove_cascade_delete
|
||||
other-modules:
|
||||
Paths_simplex_chat
|
||||
hs-source-dirs:
|
||||
|
||||
@@ -1150,45 +1150,62 @@ processChatCommand' vr = \case
|
||||
pure $ CRChatCleared user (AChatInfo SCTLocal $ LocalChat nf)
|
||||
_ -> throwCmdError "not supported"
|
||||
APIAcceptContact incognito connReqId -> withUser $ \user@User {userId} -> do
|
||||
(uclId, (ucl, gLinkInfo_)) <- withFastStore $ \db -> do
|
||||
uclId <- getUserContactLinkIdByCReq db connReqId
|
||||
uclGLinkInfo <- getUserContactLinkById db userId uclId
|
||||
pure (uclId, uclGLinkInfo)
|
||||
let UserContactLink {shortLinkDataSet, addressSettings} = ucl
|
||||
when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data"
|
||||
withUserContactLock "acceptContact" uclId $ do
|
||||
cReq@UserContactRequest {welcomeSharedMsgId} <- withFastStore $ \db -> getContactRequest db user connReqId
|
||||
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
|
||||
let contactUsed = isNothing gLinkInfo_
|
||||
ct' <- withStore' $ \db -> do
|
||||
updateContactAccepted db user ct contactUsed
|
||||
conn' <-
|
||||
if sqSecured
|
||||
then conn {connStatus = ConnSndReady} <$ updateConnectionStatusFromTo db connId ConnNew ConnSndReady
|
||||
else pure conn
|
||||
pure ct {contactUsed, activeConn = Just conn'}
|
||||
when sqSecured $ forM_ (autoReply addressSettings) $ \mc -> case welcomeSharedMsgId of
|
||||
Just smId ->
|
||||
void $ sendDirectContactMessage user ct' $ XMsgUpdate smId mc M.empty Nothing Nothing Nothing
|
||||
Nothing -> do
|
||||
(msg, _) <- sendDirectContactMessage user ct' $ XMsgNew $ MCSimple $ extMsgContent mc Nothing
|
||||
ci <- saveSndChatItem user (CDDirectSnd ct') msg (CISndMsgContent mc)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci]
|
||||
pure $ CRAcceptingContactRequest user ct'
|
||||
uclData_ <- withFastStore $ \db -> do
|
||||
uclId_ <- getUserContactLinkIdByCReq db connReqId
|
||||
forM uclId_ $ \uclId -> do -- address may be deleted
|
||||
uclGLinkInfo <- getUserContactLinkById db userId uclId
|
||||
pure (uclId, uclGLinkInfo)
|
||||
withContactRequestLock "acceptContact" connReqId $ case uclData_ of
|
||||
Nothing -> do -- address was deleted
|
||||
when incognito $ throwCmdError "incognito not allowed when address is not found"
|
||||
cReq <- withFastStore $ \db -> getContactRequest db user connReqId
|
||||
(ct, _sqSecured) <- acceptCReq user cReq True
|
||||
pure $ CRAcceptingContactRequest user ct
|
||||
Just (uclId, (ucl@UserContactLink {shortLinkDataSet}, gLinkInfo_)) -> do
|
||||
when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data"
|
||||
withUserContactLock "acceptContact" uclId $ do
|
||||
cReq <- withFastStore $ \db -> getContactRequest db user connReqId
|
||||
let contactUsed = isNothing gLinkInfo_ -- for redundancy, as group link requests are auto-accepted
|
||||
(ct, sqSecured) <- acceptCReq user cReq contactUsed
|
||||
when sqSecured $ sendWelcomeMsg user ct ucl cReq
|
||||
pure $ CRAcceptingContactRequest user ct
|
||||
where
|
||||
acceptCReq user cReq contactUsed = do
|
||||
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
|
||||
ct' <- withStore' $ \db -> do
|
||||
updateContactAccepted db user ct contactUsed
|
||||
conn' <-
|
||||
if sqSecured
|
||||
then conn {connStatus = ConnSndReady} <$ updateConnectionStatusFromTo db connId ConnNew ConnSndReady
|
||||
else pure conn
|
||||
pure ct {contactUsed, activeConn = Just conn'}
|
||||
pure (ct', sqSecured)
|
||||
sendWelcomeMsg user ct ucl UserContactRequest {welcomeSharedMsgId} =
|
||||
forM_ (autoReply $ addressSettings ucl) $ \mc -> case welcomeSharedMsgId of
|
||||
Just smId ->
|
||||
void $ sendDirectContactMessage user ct $ XMsgUpdate smId mc M.empty Nothing Nothing Nothing
|
||||
Nothing -> do
|
||||
(msg, _) <- sendDirectContactMessage user ct $ XMsgNew $ MCSimple $ extMsgContent mc Nothing
|
||||
ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
|
||||
APIRejectContact connReqId -> withUser $ \user -> do
|
||||
userContactLinkId <- withFastStore $ \db -> getUserContactLinkIdByCReq db connReqId
|
||||
withUserContactLock "rejectContact" userContactLinkId $ do
|
||||
(cReq@UserContactRequest {agentContactConnId = AgentConnId connId, agentInvitationId = AgentInvId invId}, ct_) <-
|
||||
withFastStore $ \db -> do
|
||||
cReq@UserContactRequest {contactId_} <- getContactRequest db user connReqId
|
||||
ct_ <- forM contactId_ $ \contactId -> do
|
||||
ct <- getContact db vr user contactId
|
||||
deleteContact db user ct
|
||||
pure ct
|
||||
liftIO $ deleteContactRequest db user connReqId
|
||||
pure (cReq, ct_)
|
||||
withAgent $ \a -> rejectContact a connId invId
|
||||
pure $ CRContactRequestRejected user cReq ct_
|
||||
uclId_ <- withFastStore $ \db -> getUserContactLinkIdByCReq db connReqId
|
||||
withContactRequestLock "rejectContact" connReqId $ case uclId_ of
|
||||
Nothing -> rejectCReq user -- address was deleted
|
||||
Just uclId -> withUserContactLock "rejectContact" uclId $ rejectCReq user
|
||||
where
|
||||
rejectCReq user = do
|
||||
(cReq@UserContactRequest {agentInvitationId = AgentInvId invId}, ct_) <-
|
||||
withFastStore $ \db -> do
|
||||
cReq@UserContactRequest {contactId_} <- getContactRequest db user connReqId
|
||||
ct_ <- forM contactId_ $ \contactId -> do
|
||||
ct <- getContact db vr user contactId
|
||||
deleteContact db user ct
|
||||
pure ct
|
||||
liftIO $ deleteContactRequest db user connReqId
|
||||
pure (cReq, ct_)
|
||||
withAgent (`rejectContact` invId)
|
||||
pure $ CRContactRequestRejected user cReq ct_
|
||||
APISendCallInvitation contactId callType -> withUser $ \user -> do
|
||||
-- party initiating call
|
||||
ct <- withFastStore $ \db -> getContact db vr user contactId
|
||||
@@ -2785,6 +2802,7 @@ processChatCommand' vr = \case
|
||||
CLContact ctId -> "Contact " <> tshow ctId
|
||||
CLGroup gId -> "Group " <> tshow gId
|
||||
CLUserContact ucId -> "UserContact " <> tshow ucId
|
||||
CLContactRequest crId -> "ContactRequest " <> tshow crId
|
||||
CLFile fId -> "File " <> tshow fId
|
||||
DebugEvent event -> toView event >> ok_
|
||||
GetAgentSubsTotal userId -> withUserId userId $ \user -> do
|
||||
|
||||
@@ -142,6 +142,10 @@ withUserContactLock :: Text -> Int64 -> CM a -> CM a
|
||||
withUserContactLock name = withEntityLock name . CLUserContact
|
||||
{-# INLINE withUserContactLock #-}
|
||||
|
||||
withContactRequestLock :: Text -> Int64 -> CM a -> CM a
|
||||
withContactRequestLock name = withEntityLock name . CLContactRequest
|
||||
{-# INLINE withContactRequestLock #-}
|
||||
|
||||
withFileLock :: Text -> Int64 -> CM a -> CM a
|
||||
withFileLock name = withEntityLock name . CLFile
|
||||
{-# INLINE withFileLock #-}
|
||||
@@ -878,7 +882,7 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of
|
||||
-- It may be reasonable to set it when contact is first prepared, but then we can't use it to ignore requests after acceptance,
|
||||
-- and it may lead to race conditions with XInfo events.
|
||||
acceptContactRequest :: User -> UserContactRequest -> IncognitoEnabled -> CM (Contact, Connection, SndQueueSecured)
|
||||
acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId = AgentInvId invId, contactId_, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId, pqSupport} incognito = do
|
||||
acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId = AgentInvId invId, contactId_, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId_, xContactId, pqSupport} incognito = do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let pqSup = PQSupportOn
|
||||
pqSup' = pqSup `CR.pqSupportAnd` pqSupport
|
||||
@@ -887,20 +891,20 @@ acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId =
|
||||
(ct, conn, incognitoProfile) <- case contactId_ of
|
||||
Nothing -> do
|
||||
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
|
||||
connId <- withAgent $ \a -> prepareConnectionToAccept a True invId pqSup'
|
||||
connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
|
||||
(ct, conn) <- withStore' $ \db ->
|
||||
createContactFromRequest db user userContactLinkId connId chatV cReqChatVRange cName profileId cp xContactId incognitoProfile subMode pqSup' False
|
||||
createContactFromRequest db user userContactLinkId_ connId chatV cReqChatVRange cName profileId cp xContactId incognitoProfile subMode pqSup' False
|
||||
pure (ct, conn, incognitoProfile)
|
||||
Just contactId -> do
|
||||
ct <- withFastStore $ \db -> getContact db vr user contactId
|
||||
case contactConn ct of
|
||||
Nothing -> do
|
||||
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
|
||||
connId <- withAgent $ \a -> prepareConnectionToAccept a True invId pqSup'
|
||||
connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
|
||||
currentTs <- liftIO getCurrentTime
|
||||
conn <- withStore' $ \db -> do
|
||||
forM_ xContactId $ \xcId -> setContactAcceptedXContactId db ct xcId
|
||||
createAcceptedContactConn db user userContactLinkId contactId connId chatV cReqChatVRange pqSup' incognitoProfile subMode currentTs
|
||||
createAcceptedContactConn db user userContactLinkId_ contactId connId chatV cReqChatVRange pqSup' incognitoProfile subMode currentTs
|
||||
pure (ct {activeConn = Just conn} :: Contact, conn, incognitoProfile)
|
||||
Just conn@Connection {customUserProfileId} -> do
|
||||
incognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
|
||||
@@ -908,7 +912,7 @@ acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId =
|
||||
let profileToSend = profileToSendOnAccept user incognitoProfile False
|
||||
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
|
||||
-- TODO [certs rcv]
|
||||
(ct,conn,) . fst <$> withAgent (\a -> acceptContact a (aConnId conn) True invId dm pqSup' subMode)
|
||||
(ct,conn,) . fst <$> withAgent (\a -> acceptContact a (aUserId user) (aConnId conn) True invId dm pqSup' subMode)
|
||||
|
||||
acceptContactRequestAsync :: User -> Int64 -> Contact -> UserContactRequest -> Maybe IncognitoProfile -> CM Contact
|
||||
acceptContactRequestAsync
|
||||
@@ -925,7 +929,7 @@ acceptContactRequestAsync
|
||||
currentTs <- liftIO getCurrentTime
|
||||
withStore $ \db -> do
|
||||
forM_ xContactId $ \xcId -> liftIO $ setContactAcceptedXContactId db ct xcId
|
||||
Connection {connId} <- liftIO $ createAcceptedContactConn db user uclId contactId acId chatV cReqChatVRange cReqPQSup incognitoProfile subMode currentTs
|
||||
Connection {connId} <- liftIO $ createAcceptedContactConn db user (Just uclId) contactId acId chatV cReqChatVRange cReqPQSup incognitoProfile subMode currentTs
|
||||
liftIO $ setCommandConnId db user cmdId connId
|
||||
getContact db vr user contactId
|
||||
|
||||
@@ -2194,7 +2198,7 @@ agentAcceptContactAsync :: MsgEncodingI e => User -> Bool -> InvitationId -> Cha
|
||||
agentAcceptContactAsync user enableNtfs invId msg subMode pqSup chatV = do
|
||||
cmdId <- withStore' $ \db -> createCommand db user Nothing CFAcceptContact
|
||||
dm <- encodeConnInfoPQ pqSup chatV msg
|
||||
connId <- withAgent $ \a -> acceptContactAsync a (aCorrId cmdId) enableNtfs invId dm pqSup subMode
|
||||
connId <- withAgent $ \a -> acceptContactAsync a (aUserId user) (aCorrId cmdId) enableNtfs invId dm pqSup subMode
|
||||
pure (cmdId, connId)
|
||||
|
||||
deleteAgentConnectionAsync :: ConnId -> CM ()
|
||||
|
||||
@@ -143,12 +143,11 @@ createOrUpdateContactRequest
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c USING (user_contact_link_id)
|
||||
JOIN contact_profiles p USING (contact_profile_id)
|
||||
WHERE cr.user_id = ?
|
||||
AND cr.xcontact_id = ?
|
||||
|
||||
@@ -708,7 +708,7 @@ getUserContacts db vr user@User {userId} = do
|
||||
contacts <- rights <$> mapM (runExceptT . getContact db vr user) contactIds
|
||||
pure $ filter (\Contact {activeConn} -> isJust activeConn) contacts
|
||||
|
||||
getUserContactLinkIdByCReq :: DB.Connection -> Int64 -> ExceptT StoreError IO Int64
|
||||
getUserContactLinkIdByCReq :: DB.Connection -> Int64 -> ExceptT StoreError IO (Maybe Int64)
|
||||
getUserContactLinkIdByCReq db contactRequestId =
|
||||
ExceptT . firstRow fromOnly (SEContactRequestNotFound contactRequestId) $
|
||||
DB.query db "SELECT user_contact_link_id FROM contact_requests WHERE contact_request_id = ?" (Only contactRequestId)
|
||||
@@ -734,12 +734,11 @@ contactRequestQuery =
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c USING (user_contact_link_id)
|
||||
JOIN contact_profiles p USING (contact_profile_id)
|
||||
|]
|
||||
|
||||
@@ -774,8 +773,8 @@ deleteContactRequest db User {userId} contactRequestId = do
|
||||
(userId, userId, contactRequestId, userId)
|
||||
DB.execute db "DELETE FROM contact_requests WHERE user_id = ? AND contact_request_id = ?" (userId, contactRequestId)
|
||||
|
||||
createContactFromRequest :: DB.Connection -> User -> Int64 -> ConnId -> VersionChat -> VersionRangeChat -> ContactName -> ProfileId -> Profile -> Maybe XContactId -> Maybe IncognitoProfile -> SubscriptionMode -> PQSupport -> Bool -> IO (Contact, Connection)
|
||||
createContactFromRequest db user@User {userId, profile = LocalProfile {preferences}} uclId agentConnId connChatVersion cReqChatVRange localDisplayName profileId profile xContactId incognitoProfile subMode pqSup contactUsed = do
|
||||
createContactFromRequest :: DB.Connection -> User -> Maybe Int64 -> ConnId -> VersionChat -> VersionRangeChat -> ContactName -> ProfileId -> Profile -> Maybe XContactId -> Maybe IncognitoProfile -> SubscriptionMode -> PQSupport -> Bool -> IO (Contact, Connection)
|
||||
createContactFromRequest db user@User {userId, profile = LocalProfile {preferences}} uclId_ agentConnId connChatVersion cReqChatVRange localDisplayName profileId profile xContactId incognitoProfile subMode pqSup contactUsed = do
|
||||
currentTs <- getCurrentTime
|
||||
let userPreferences = fromMaybe emptyChatPrefs $ incognitoProfile >> preferences
|
||||
DB.execute
|
||||
@@ -784,7 +783,7 @@ createContactFromRequest db user@User {userId, profile = LocalProfile {preferenc
|
||||
(userId, localDisplayName, profileId, BI True, userPreferences, currentTs, currentTs, currentTs, xContactId, BI contactUsed)
|
||||
contactId <- insertedRowId db
|
||||
DB.execute db "UPDATE contact_requests SET contact_id = ? WHERE user_id = ? AND local_display_name = ?" (contactId, userId, localDisplayName)
|
||||
conn <- createAcceptedContactConn db user uclId contactId agentConnId connChatVersion cReqChatVRange pqSup incognitoProfile subMode currentTs
|
||||
conn <- createAcceptedContactConn db user uclId_ contactId agentConnId connChatVersion cReqChatVRange pqSup incognitoProfile subMode currentTs
|
||||
let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
||||
ct =
|
||||
Contact
|
||||
@@ -813,12 +812,12 @@ createContactFromRequest db user@User {userId, profile = LocalProfile {preferenc
|
||||
}
|
||||
pure (ct, conn)
|
||||
|
||||
createAcceptedContactConn :: DB.Connection -> User -> Int64 -> ContactId -> ConnId -> VersionChat -> VersionRangeChat -> PQSupport -> Maybe IncognitoProfile -> SubscriptionMode -> UTCTime -> IO Connection
|
||||
createAcceptedContactConn db User {userId} uclId contactId agentConnId connChatVersion cReqChatVRange pqSup incognitoProfile subMode currentTs = do
|
||||
createAcceptedContactConn :: DB.Connection -> User -> Maybe Int64 -> ContactId -> ConnId -> VersionChat -> VersionRangeChat -> PQSupport -> Maybe IncognitoProfile -> SubscriptionMode -> UTCTime -> IO Connection
|
||||
createAcceptedContactConn db User {userId} uclId_ contactId agentConnId connChatVersion cReqChatVRange pqSup incognitoProfile subMode currentTs = do
|
||||
customUserProfileId <- forM incognitoProfile $ \case
|
||||
NewIncognito p -> createIncognitoProfile_ db userId currentTs p
|
||||
ExistingIncognito LocalProfile {profileId = pId} -> pure pId
|
||||
createConnection_ db userId ConnContact (Just contactId) agentConnId ConnNew connChatVersion cReqChatVRange Nothing (Just uclId) customUserProfileId 0 currentTs subMode pqSup
|
||||
createConnection_ db userId ConnContact (Just contactId) agentConnId ConnNew connChatVersion cReqChatVRange Nothing uclId_ customUserProfileId 0 currentTs subMode pqSup
|
||||
|
||||
updateContactAccepted :: DB.Connection -> User -> Contact -> Bool -> IO ()
|
||||
updateContactAccepted db User {userId} Contact {contactId} contactUsed =
|
||||
|
||||
@@ -1051,12 +1051,11 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
||||
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
||||
WHERE cr.user_id = ?
|
||||
|
||||
@@ -9,6 +9,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20250402_short_links
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250512_member_admission
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250513_group_scope
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250526_short_links
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250702_contact_requests_remove_cascade_delete
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
@@ -17,7 +18,8 @@ schemaMigrations =
|
||||
("20250402_short_links", m20250402_short_links, Just down_m20250402_short_links),
|
||||
("20250512_member_admission", m20250512_member_admission, Just down_m20250512_member_admission),
|
||||
("20250513_group_scope", m20250513_group_scope, Just down_m20250513_group_scope),
|
||||
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links)
|
||||
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links),
|
||||
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20250702_contact_requests_remove_cascade_delete where
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20250702_contact_requests_remove_cascade_delete :: Text
|
||||
m20250702_contact_requests_remove_cascade_delete =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE contact_requests DROP CONSTRAINT contact_requests_user_contact_link_id_fkey;
|
||||
|
||||
ALTER TABLE contact_requests ALTER COLUMN user_contact_link_id DROP NOT NULL;
|
||||
|
||||
ALTER TABLE contact_requests
|
||||
ADD CONSTRAINT contact_requests_user_contact_link_id_fkey
|
||||
FOREIGN KEY (user_contact_link_id)
|
||||
REFERENCES user_contact_links(user_contact_link_id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE SET NULL;
|
||||
|]
|
||||
|
||||
down_m20250702_contact_requests_remove_cascade_delete :: Text
|
||||
down_m20250702_contact_requests_remove_cascade_delete =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE contact_requests DROP CONSTRAINT contact_requests_user_contact_link_id_fkey;
|
||||
|
||||
ALTER TABLE contact_requests ALTER COLUMN user_contact_link_id SET NOT NULL;
|
||||
|
||||
ALTER TABLE contact_requests
|
||||
ADD CONSTRAINT contact_requests_user_contact_link_id_fkey
|
||||
FOREIGN KEY (user_contact_link_id)
|
||||
REFERENCES user_contact_links(user_contact_link_id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE;
|
||||
|]
|
||||
@@ -349,7 +349,7 @@ ALTER TABLE test_chat_schema.contact_profiles ALTER COLUMN contact_profile_id AD
|
||||
|
||||
CREATE TABLE test_chat_schema.contact_requests (
|
||||
contact_request_id bigint NOT NULL,
|
||||
user_contact_link_id bigint NOT NULL,
|
||||
user_contact_link_id bigint,
|
||||
agent_invitation_id bytea NOT NULL,
|
||||
contact_profile_id bigint,
|
||||
local_display_name text NOT NULL,
|
||||
@@ -2313,7 +2313,7 @@ ALTER TABLE ONLY test_chat_schema.contact_requests
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.contact_requests
|
||||
ADD CONSTRAINT contact_requests_user_contact_link_id_fkey FOREIGN KEY (user_contact_link_id) REFERENCES test_chat_schema.user_contact_links(user_contact_link_id) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
ADD CONSTRAINT contact_requests_user_contact_link_id_fkey FOREIGN KEY (user_contact_link_id) REFERENCES test_chat_schema.user_contact_links(user_contact_link_id) ON UPDATE CASCADE ON DELETE SET NULL;
|
||||
|
||||
|
||||
|
||||
@@ -2689,3 +2689,6 @@ ALTER TABLE ONLY test_chat_schema.user_contact_links
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.xftp_file_descriptions
|
||||
ADD CONSTRAINT xftp_file_descriptions_user_id_fkey FOREIGN KEY (user_id) REFERENCES test_chat_schema.users(user_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20250402_short_links
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250512_member_admission
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250513_group_scope
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250526_short_links
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250702_contact_requests_remove_cascade_delete
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -263,7 +264,8 @@ schemaMigrations =
|
||||
("20250402_short_links", m20250402_short_links, Just down_m20250402_short_links),
|
||||
("20250512_member_admission", m20250512_member_admission, Just down_m20250512_member_admission),
|
||||
("20250513_group_scope", m20250513_group_scope, Just down_m20250513_group_scope),
|
||||
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links)
|
||||
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links),
|
||||
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250702_contact_requests_remove_cascade_delete where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20250702_contact_requests_remove_cascade_delete :: Query
|
||||
m20250702_contact_requests_remove_cascade_delete =
|
||||
[sql|
|
||||
PRAGMA writable_schema=1;
|
||||
|
||||
UPDATE sqlite_master
|
||||
SET sql = replace(
|
||||
replace(
|
||||
sql,
|
||||
'user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links',
|
||||
'user_contact_link_id INTEGER REFERENCES user_contact_links'
|
||||
),
|
||||
'ON UPDATE CASCADE ON DELETE CASCADE,',
|
||||
'ON UPDATE CASCADE ON DELETE SET NULL,'
|
||||
)
|
||||
WHERE name = 'contact_requests' AND type = 'table';
|
||||
|
||||
PRAGMA writable_schema=0;
|
||||
|]
|
||||
|
||||
down_m20250702_contact_requests_remove_cascade_delete :: Query
|
||||
down_m20250702_contact_requests_remove_cascade_delete =
|
||||
[sql|
|
||||
PRAGMA writable_schema=1;
|
||||
|
||||
UPDATE sqlite_master
|
||||
SET sql = replace(
|
||||
replace(
|
||||
sql,
|
||||
'ON UPDATE CASCADE ON DELETE SET NULL,',
|
||||
'ON UPDATE CASCADE ON DELETE CASCADE,'
|
||||
),
|
||||
'user_contact_link_id INTEGER REFERENCES user_contact_links',
|
||||
'user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links'
|
||||
)
|
||||
WHERE name = 'contact_requests' AND type = 'table';
|
||||
|
||||
PRAGMA writable_schema=0;
|
||||
|]
|
||||
@@ -274,7 +274,7 @@ Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO conn_invitations
|
||||
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted) VALUES (?, ?, ?, ?, 0);
|
||||
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted) VALUES (?, ?, ?, ?, 0);
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -822,7 +822,7 @@ Query: DELETE FROM commands WHERE command_id = ?
|
||||
Plan:
|
||||
SEARCH commands USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: DELETE FROM conn_invitations WHERE contact_conn_id = ? AND invitation_id = ?
|
||||
Query: DELETE FROM conn_invitations WHERE invitation_id = ?
|
||||
Plan:
|
||||
SEARCH conn_invitations USING PRIMARY KEY (invitation_id=?)
|
||||
|
||||
|
||||
@@ -398,12 +398,11 @@ Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c USING (user_contact_link_id)
|
||||
JOIN contact_profiles p USING (contact_profile_id)
|
||||
WHERE cr.user_id = ?
|
||||
AND cr.xcontact_id = ?
|
||||
@@ -412,7 +411,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH cr USING INDEX idx_contact_requests_updated_at (user_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT COUNT(1)
|
||||
@@ -1631,12 +1629,11 @@ Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
||||
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
||||
WHERE cr.user_id = ?
|
||||
@@ -1655,18 +1652,16 @@ Plan:
|
||||
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
SEARCH cr USING INDEX idx_contact_requests_updated_at (user_id=? AND updated_at<?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
||||
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
||||
WHERE cr.user_id = ?
|
||||
@@ -1685,18 +1680,16 @@ Plan:
|
||||
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
SEARCH cr USING INDEX idx_contact_requests_updated_at (user_id=? AND updated_at>?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
||||
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
||||
WHERE cr.user_id = ?
|
||||
@@ -1715,7 +1708,6 @@ Plan:
|
||||
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
SEARCH cr USING INDEX idx_contact_requests_updated_at (user_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -4742,35 +4734,31 @@ Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c USING (user_contact_link_id)
|
||||
JOIN contact_profiles p USING (contact_profile_id)
|
||||
WHERE cr.user_id = ? AND cr.business_group_id = ?
|
||||
Plan:
|
||||
SEARCH cr USING INDEX idx_contact_requests_business_group_id (business_group_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
|
||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c USING (user_contact_link_id)
|
||||
JOIN contact_profiles p USING (contact_profile_id)
|
||||
WHERE cr.user_id = ? AND cr.contact_request_id = ?
|
||||
Plan:
|
||||
SEARCH cr USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
|
||||
@@ -344,8 +344,8 @@ CREATE TABLE user_contact_links(
|
||||
);
|
||||
CREATE TABLE contact_requests(
|
||||
contact_request_id INTEGER PRIMARY KEY,
|
||||
user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
user_contact_link_id INTEGER REFERENCES user_contact_links
|
||||
ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles
|
||||
ON DELETE SET NULL
|
||||
|
||||
@@ -64,6 +64,7 @@ data ChatLockEntity
|
||||
| CLContact ContactId
|
||||
| CLGroup GroupId
|
||||
| CLUserContact Int64
|
||||
| CLContactRequest Int64
|
||||
| CLFile Int64
|
||||
deriving (Eq, Ord)
|
||||
|
||||
@@ -488,13 +489,13 @@ getProfileById db userId profileId =
|
||||
toProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) -> LocalProfile
|
||||
toProfile (displayName, fullName, image, contactLink, localAlias, preferences) = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Int64) :. (AgentConnId, Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
||||
|
||||
toContactRequest :: ContactRequestRow -> UserContactRequest
|
||||
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId) :. (agentContactConnId, profileId, displayName, fullName, image, contactLink) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
|
||||
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, image, contactLink) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
|
||||
let profile = Profile {displayName, fullName, image, contactLink, preferences}
|
||||
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId, agentContactConnId, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
|
||||
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
|
||||
|
||||
userQuery :: Query
|
||||
userQuery =
|
||||
|
||||
@@ -347,8 +347,7 @@ data UserContactRequest = UserContactRequest
|
||||
agentInvitationId :: AgentInvId,
|
||||
contactId_ :: Maybe ContactId,
|
||||
businessGroupId_ :: Maybe GroupId,
|
||||
userContactLinkId :: Int64,
|
||||
agentContactConnId :: AgentConnId, -- connection id of user contact
|
||||
userContactLinkId_ :: Maybe Int64,
|
||||
cReqChatVRange :: VersionRangeChat,
|
||||
localDisplayName :: ContactName,
|
||||
profileId :: Int64,
|
||||
|
||||
@@ -48,10 +48,7 @@ chatProfileTests = do
|
||||
it "deduplicate contact requests" testDeduplicateContactRequests
|
||||
it "deduplicate contact requests with profile change" testDeduplicateContactRequestsProfileChange
|
||||
it "reject contact and delete contact link" testRejectContactAndDeleteUserContact
|
||||
-- TODO [short links] fix address deletion:
|
||||
-- TODO - either alert user that N chats will be deleted and delete contact request contacts and business chats
|
||||
-- TODO - or allow to accept contact requests for deleted address (remove cascade deletes, rework agent)
|
||||
xit "delete connection requests when contact link deleted" testDeleteConnectionRequests
|
||||
it "keep connection requests when contact link deleted" testKeepConnectionRequests
|
||||
it "connected contact works when contact link deleted" testContactLinkDeletedConnectedContactWorks
|
||||
-- TODO [short links] test auto-reply with current version, with connecting client not preparing contact
|
||||
it "auto-reply message" testAutoReplyMessage
|
||||
@@ -673,8 +670,8 @@ testRejectContactAndDeleteUserContact = testChat3 aliceProfile bobProfile cathPr
|
||||
cath ##> ("/c " <> cLink)
|
||||
cath <## "error: connection authorization failed - this could happen if connection was deleted, secured with different credentials, or due to a bug - please re-create the connection"
|
||||
|
||||
testDeleteConnectionRequests :: HasCallStack => TestParams -> IO ()
|
||||
testDeleteConnectionRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
testKeepConnectionRequests :: HasCallStack => TestParams -> IO ()
|
||||
testKeepConnectionRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
\alice bob cath -> do
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
@@ -687,14 +684,52 @@ testDeleteConnectionRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
alice <## "Your chat address is deleted - accepted contacts will remain connected."
|
||||
alice <## "To create a new chat address use /ad"
|
||||
|
||||
-- can accept and reject requests after address deletion
|
||||
alice ##> "/ac bob"
|
||||
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
(alice <## "bob (Bob): contact is connected")
|
||||
alice <##> bob
|
||||
|
||||
alice ##> "/rc cath"
|
||||
alice <## "cath: contact request rejected"
|
||||
|
||||
alice @@@ [("@bob", "hey")]
|
||||
|
||||
-- bob's request to new address uses different name
|
||||
alice ##> "/ad"
|
||||
cLink' <- getContactLink alice True
|
||||
|
||||
bob ##> ("/c " <> cLink')
|
||||
-- same names are used here, as they were released at /da
|
||||
alice <#? bob
|
||||
bob <## "connection request sent!"
|
||||
alice <## "bob_1 (Bob) wants to connect to you!"
|
||||
alice <## "to accept: /ac bob_1"
|
||||
alice <## "to reject: /rc bob_1 (the sender will NOT be notified)"
|
||||
|
||||
alice ##> "/ac bob_1"
|
||||
alice <## "bob_1 (Bob): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(bob <## "alice_1 (Alice): contact is connected")
|
||||
(alice <## "bob_1 (Bob): contact is connected")
|
||||
|
||||
alice #> "@bob_1 hi"
|
||||
bob <# "alice_1> hi"
|
||||
bob #> "@alice_1 hey"
|
||||
alice <# "bob_1> hey"
|
||||
|
||||
cath ##> ("/c " <> cLink')
|
||||
alice <#? cath
|
||||
|
||||
alice ##> "/ac cath"
|
||||
alice <## "cath (Catherine): accepting contact request, you can send messages to contact"
|
||||
concurrently_
|
||||
(cath <## "alice (Alice): contact is connected")
|
||||
(alice <## "cath (Catherine): contact is connected")
|
||||
alice <##> cath
|
||||
|
||||
alice @@@ [("@cath", "hey"), ("@bob_1", "hey"), ("@bob", "hey")]
|
||||
|
||||
testContactLinkDeletedConnectedContactWorks :: HasCallStack => TestParams -> IO ()
|
||||
testContactLinkDeletedConnectedContactWorks = testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
|
||||
Reference in New Issue
Block a user