core, ui: prohibit changing profile for prepared entity when first attempt to connect failed (#6037)

* core: prohibit changing profile for prepared entity when first attempt to connect failed

* reuse incognito

* schema

* ios

* postgres schema

* ios

* reenable tests

* kotlin

* update alert

* rename predicate, combine queries

* send the correct incognito mode, fail on attempt to change mode for prepared connection

* query plans

* ui: show group connecting status

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
spaced4ndy
2025-07-05 10:09:10 +00:00
committed by GitHub
parent e5d3029edf
commit 2dd54c6697
48 changed files with 425 additions and 133 deletions
+1
View File
@@ -768,6 +768,7 @@ data ChatEvent
| CEvtGroupMemberSwitch {user :: User, groupInfo :: GroupInfo, member :: GroupMember, switchProgress :: SwitchProgress}
| CEvtContactRatchetSync {user :: User, contact :: Contact, ratchetSyncProgress :: RatchetSyncProgress}
| CEvtGroupMemberRatchetSync {user :: User, groupInfo :: GroupInfo, member :: GroupMember, ratchetSyncProgress :: RatchetSyncProgress}
| CEvtChatInfoUpdated {user :: User, chatInfo :: AChatInfo}
| CEvtNewChatItems {user :: User, chatItems :: [AChatItem]} -- there is the same command response
| CEvtChatItemsStatusesUpdated {user :: User, chatItems :: [AChatItem]}
| CEvtChatItemUpdated {user :: User, chatItem :: AChatItem} -- there is the same command response
+40 -40
View File
@@ -1171,7 +1171,7 @@ processChatCommand' vr = \case
pure $ CRAcceptingContactRequest user ct
where
acceptCReq user cReq contactUsed = do
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
(ct, conn, sqSecured) <- acceptContactRequest user cReq incognito
ct' <- withStore' $ \db -> do
updateContactAccepted db user ct contactUsed
conn' <-
@@ -1820,7 +1820,12 @@ processChatCommand' vr = \case
case preparedContact of
Nothing -> throwCmdError "contact doesn't have link to connect"
Just PreparedContact {connLinkToConnect = ACCL SCMInvitation ccLink} -> do
(_, customUserProfile) <- connectViaInvitation user incognito ccLink (Just contactId)
(_, customUserProfile) <- connectViaInvitation user incognito ccLink (Just contactId) `catchChatError` \e -> do
-- get updated contact, in case connection was started - in UI it would lock ability to change
-- user or incognito profile for contact, 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
-- get updated contact with connection
ct' <- withFastStore $ \db -> getContact db vr user contactId
forM_ msgContent_ $ \mc -> do
@@ -1836,7 +1841,13 @@ processChatCommand' vr = \case
smId <- getSharedMsgId
withFastStore' $ \db -> setRequestSharedMsgIdForContact db contactId smId
pure (smId, mc)
connectViaContact user (Just $ PCEContact ct) incognito ccLink welcomeSharedMsgId msg_ >>= \case
r <- connectViaContact user (Just $ PCEContact ct) incognito ccLink welcomeSharedMsgId msg_ `catchChatError` \e -> do
-- get updated contact, in case connection was started - in UI it would lock ability to change
-- user or incognito profile for contact, 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
case r of
CVRSentInvitation _conn customUserProfile -> do
-- get updated contact with connection
ct' <- withFastStore $ \db -> getContact db vr user contactId
@@ -1856,7 +1867,13 @@ processChatCommand' vr = \case
smId <- getSharedMsgId
withFastStore' $ \db -> setRequestSharedMsgIdForGroup db groupId smId
pure (smId, mc)
connectViaContact user (Just $ PCEGroup gInfo hostMember) incognito connLinkToConnect welcomeSharedMsgId msg_ >>= \case
r <- connectViaContact user (Just $ PCEGroup gInfo hostMember) incognito connLinkToConnect welcomeSharedMsgId msg_ `catchChatError` \e -> do
-- get updated group info, in case connection was started (connLinkPreparedConnection) - in UI it would lock ability to change
-- user or incognito profile for group or business chat, in case server received request while client got network error
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
toView $ CEvtChatInfoUpdated user (AChatInfo SCTGroup $ GroupChat gInfo' Nothing)
throwError e
case r of
CVRSentInvitation _conn customUserProfile -> do
-- get updated group info (connLinkStartedConnection and incognito membership)
gInfo' <- withFastStore $ \db -> do
@@ -2914,32 +2931,30 @@ processChatCommand' vr = \case
connectViaInvitation user@User {userId} incognito (CCLink cReq@(CRInvitationUri crData e2e) sLnk_) contactId_ =
withInvitationLock "connect" (strEncode cReq) $ do
subMode <- chatReadVar subscriptionMode
-- [incognito] generate profile to send
-- TODO [short links] if incognito profile was prepared on the previous attempt, it should be used instead of creating a new one
-- TODO [short links] for connection via prepared contacts we need to:
-- - potentially use different flow or pass contact as parameter here,
-- - prohibit changing user/incognito on the second attempt in the UI
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
let profileToSend = userProfileToSend user incognitoProfile Nothing False
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOn cReq) >>= \case
Nothing -> throwChatError CEInvalidConnReq
-- TODO PQ the error above should be CEIncompatibleConnReqVersion, also the same API should be called in Plan
Just (agentV, pqSup') -> do
let chatV = agentToChatVersion agentV
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
withFastStore' (\db -> getConnectionEntityByConnReq db vr user cReqs) >>= \case
Nothing -> joinNewConn chatV dm
Just (RcvDirectMsgConnection conn@Connection {connStatus, contactConnInitiated} _ct_)
| connStatus == ConnNew && contactConnInitiated -> joinNewConn chatV dm -- own connection link
| connStatus == ConnPrepared -> joinPreparedConn conn dm -- retrying join after error
Nothing -> joinNewConn chatV
Just (RcvDirectMsgConnection conn@Connection {connStatus, contactConnInitiated, customUserProfileId} _ct_)
| connStatus == ConnNew && contactConnInitiated -> joinNewConn chatV -- own connection link
| connStatus == ConnPrepared -> do -- retrying join after error
localIncognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
joinPreparedConn conn (fromLocalProfile <$> localIncognitoProfile) chatV
Just ent -> throwCmdError $ "connection is not RcvDirectMsgConnection: " <> show (connEntityInfo ent)
where
joinNewConn chatV dm = do
joinNewConn chatV = do
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
conn <- withFastStore' $ \db -> createDirectConnection' db userId connId ccLink contactId_ ConnPrepared (incognitoProfile $> profileToSend) subMode chatV pqSup'
joinPreparedConn conn dm
joinPreparedConn conn@Connection {connId = dbConnId} dm = do
conn <- withFastStore' $ \db -> createDirectConnection' db userId connId ccLink contactId_ ConnPrepared incognitoProfile subMode chatV pqSup'
joinPreparedConn conn incognitoProfile chatV
joinPreparedConn conn incognitoProfile chatV = do
let profileToSend = userProfileToSend user incognitoProfile Nothing False
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a (aUserId user) (aConnId conn) True cReq dm pqSup' subMode
let newStatus = if sqSecured then ConnSndReady else ConnJoined
conn' <- withFastStore' $ \db -> updateConnectionStatusFromTo db conn ConnPrepared newStatus
@@ -2984,28 +2999,13 @@ processChatCommand' vr = \case
Just Connection {xContactId} -> connect' groupLinkId cReqHash1 xContactId
Nothing -> connect' groupLinkId cReqHash1 Nothing
where
joinPreparedConn' xContactId_ conn@Connection {customUserProfileId = pId_} inGroup = do
joinPreparedConn' xContactId_ conn@Connection {customUserProfileId} inGroup = do
when (incognito /= isJust customUserProfileId) $ throwCmdError "incognito mode is different from prepared connection"
xContactId <- mkXContactId xContactId_
incognitoProfile <- getOrCreateIncognitoProfile
localIncognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
let incognitoProfile = fromLocalProfile <$> localIncognitoProfile
conn' <- joinContact user conn cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup PQSupportOn
pure $ CVRSentInvitation conn' incognitoProfile
where
getOrCreateIncognitoProfile
| incognito =
withStore' $ \db -> case pId_ of
Nothing -> newIncognitoProfile db
Just pId ->
runExceptT (getProfileById db userId pId)
>>= either (\_ -> newIncognitoProfile db) (pure . Just . fromLocalProfile)
| otherwise = do
when (isJust pId_) $ withStore' $ \db ->
deleteIncognitoConnectionProfile db userId conn
pure Nothing
newIncognitoProfile db = do
p <- generateRandomProfile
createdAt <- liftIO getCurrentTime
void $ createIncognitoProfile_ db userId createdAt p
pure $ Just p
mkXContactId = maybe (XContactId <$> drgRandomBytes 16) pure
connect' groupLinkId cReqHash xContactId_ = do
let inGroup = isJust groupLinkId
@@ -3045,7 +3045,7 @@ processChatCommand' vr = \case
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup
pure (connId, chatV)
joinContact :: User -> Connection -> ConnReqContact -> Maybe Profile -> XContactId -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> Bool -> PQSupport -> CM Connection
joinContact user conn@Connection {connId = dbConnId, connChatVersion = chatV} cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup pqSup = do
joinContact user conn@Connection {connChatVersion = chatV} cReq incognitoProfile xContactId welcomeSharedMsgId msg_ inGroup pqSup = do
let profileToSend = userProfileToSend user incognitoProfile Nothing inGroup
dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend (Just xContactId) welcomeSharedMsgId msg_)
subMode <- chatReadVar subscriptionMode
+4 -4
View File
@@ -909,7 +909,7 @@ acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId =
Just conn@Connection {customUserProfileId} -> do
incognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
pure (ct, conn, ExistingIncognito <$> incognitoProfile)
let profileToSend = profileToSendOnAccept user incognitoProfile False
let profileToSend = userProfileToSend' user incognitoProfile (Just ct) False
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
-- TODO [certs rcv]
(ct,conn,) . fst <$> withAgent (\a -> acceptContact a (aUserId user) (aConnId conn) True invId dm pqSup' subMode)
@@ -922,7 +922,7 @@ acceptContactRequestAsync
UserContactRequest {agentInvitationId = AgentInvId cReqInvId, cReqChatVRange, xContactId, pqSupport = cReqPQSup}
incognitoProfile = do
subMode <- chatReadVar subscriptionMode
let profileToSend = profileToSendOnAccept user incognitoProfile False
let profileToSend = userProfileToSend' user incognitoProfile (Just ct) False
vr <- chatVersionRange
let chatV = vr `peerConnChatVersion` cReqChatVRange
(cmdId, acId) <- agentAcceptContactAsync user True cReqInvId (XInfo profileToSend) subMode cReqPQSup chatV
@@ -951,7 +951,7 @@ acceptGroupJoinRequestAsync
(groupMemberId, memberId) <- withStore $ \db ->
createJoiningMember db gVar user gInfo cReqChatVRange cReqProfile cReqXContactId_ welcomeMsgId_ gLinkMemRole initialStatus
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
let Profile {displayName} = profileToSendOnAccept user incognitoProfile True
let Profile {displayName} = userProfileToSend' user incognitoProfile Nothing True
GroupMember {memberRole = userRole, memberId = userMemberId} = membership
msg =
XGrpLinkInv $
@@ -1010,7 +1010,7 @@ acceptBusinessJoinRequestAsync
clientMember@GroupMember {groupMemberId, memberId}
UserContactRequest {agentInvitationId = AgentInvId cReqInvId, cReqChatVRange, xContactId} = do
vr <- chatVersionRange
let userProfile@Profile {displayName, preferences} = profileToSendOnAccept user Nothing True
let userProfile@Profile {displayName, preferences} = userProfileToSend' user Nothing Nothing True
-- TODO [short links] take groupPreferences from group info
groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs preferences
msg =
+2 -2
View File
@@ -849,8 +849,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
when (groupFeatureAllowed SGFHistory gInfo'' && not memberIsCustomer) $ sendHistory user gInfo'' m'
where
sendXGrpLinkMem gInfo'' = do
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo''
profileToSend = profileToSendOnAccept user profileMode True
let incognitoProfile = ExistingIncognito <$> incognitoMembershipProfile gInfo''
profileToSend = userProfileToSend' user incognitoProfile Nothing True
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId
_ -> do
unless (memberPending m) $ withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected
+1 -1
View File
@@ -140,7 +140,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
+1 -1
View File
@@ -202,7 +202,7 @@ createOrUpdateContactRequest
ct <- getContact db vr user contactId
pure $ RSCurrentRequest Nothing ucr (Just $ REContact ct)
createBusinessChat = do
let Profile {preferences = userPreferences} = profileToSendOnAccept user Nothing True
let Profile {preferences = userPreferences} = userProfileToSend' user Nothing Nothing True
groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs userPreferences
(gInfo@GroupInfo {groupId}, clientMember) <-
createBusinessRequestGroup db vr gVar user cReqChatVRange profile profileId ldn groupPreferences
+6 -3
View File
@@ -176,7 +176,7 @@ createConnReqConnection db userId acId preparedEntity_ cReqHash sLnk xContactId
)
connId <- insertedRowId db
case preparedEntity_ of
Just (PCEGroup gInfo _) -> updatePreparedGroup gInfo connId customUserProfileId currentTs
Just (PCEGroup gInfo _) -> updatePreparedGroup gInfo customUserProfileId currentTs
_ -> pure ()
pure
Connection
@@ -214,8 +214,11 @@ createConnReqConnection db userId acId preparedEntity_ cReqHash sLnk xContactId
Just (PCEContact Contact {contactId}) -> (ConnContact, Just contactId, Nothing, Just contactId)
Just (PCEGroup _ GroupMember {groupMemberId}) -> (ConnMember, Nothing, Just groupMemberId, Just groupMemberId)
Nothing -> (ConnContact, Nothing, Nothing, Nothing)
updatePreparedGroup GroupInfo {groupId, membership} pccConnId customUserProfileId currentTs = do
setViaGroupLinkHash db groupId pccConnId
updatePreparedGroup GroupInfo {groupId, membership} customUserProfileId currentTs = do
DB.execute
db
"UPDATE groups SET via_group_link_uri_hash = ?, conn_link_prepared_connection = ?, updated_at = ? WHERE group_id = ?"
(cReqHash, BI True, currentTs, groupId)
when (isJust customUserProfileId) $
DB.execute
db
+2 -2
View File
@@ -939,7 +939,7 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
@@ -1821,7 +1821,7 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
@@ -10,6 +10,7 @@ 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.Chat.Store.Postgres.Migrations.M20250704_groups_conn_link_prepared_connection
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Text, Maybe Text)]
@@ -19,7 +20,8 @@ schemaMigrations =
("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),
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete)
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete),
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,21 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.Postgres.Migrations.M20250704_groups_conn_link_prepared_connection where
import Data.Text (Text)
import qualified Data.Text as T
import Text.RawString.QQ (r)
m20250704_groups_conn_link_prepared_connection :: Text
m20250704_groups_conn_link_prepared_connection =
T.pack
[r|
ALTER TABLE groups ADD COLUMN conn_link_prepared_connection SMALLINT NOT NULL DEFAULT 0;
|]
down_m20250704_groups_conn_link_prepared_connection :: Text
down_m20250704_groups_conn_link_prepared_connection =
T.pack
[r|
ALTER TABLE groups DROP COLUMN conn_link_prepared_connection;
|]
@@ -645,7 +645,8 @@ CREATE TABLE test_chat_schema.groups (
conn_short_link_to_connect bytea,
conn_link_started_connection smallint DEFAULT 0 NOT NULL,
welcome_shared_msg_id bytea,
request_shared_msg_id bytea
request_shared_msg_id bytea,
conn_link_prepared_connection smallint DEFAULT 0 NOT NULL
);
+3 -1
View File
@@ -133,6 +133,7 @@ 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.Chat.Store.SQLite.Migrations.M20250704_groups_conn_link_prepared_connection
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Query, Maybe Query)]
@@ -265,7 +266,8 @@ schemaMigrations =
("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),
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete)
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete),
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,18 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.SQLite.Migrations.M20250704_groups_conn_link_prepared_connection where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20250704_groups_conn_link_prepared_connection :: Query
m20250704_groups_conn_link_prepared_connection =
[sql|
ALTER TABLE groups ADD COLUMN conn_link_prepared_connection INTEGER NOT NULL DEFAULT 0;
|]
down_m20250704_groups_conn_link_prepared_connection :: Query
down_m20250704_groups_conn_link_prepared_connection =
[sql|
ALTER TABLE groups DROP COLUMN conn_link_prepared_connection;
|]
@@ -63,7 +63,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
@@ -966,7 +966,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
@@ -1016,7 +1016,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
@@ -4682,7 +4682,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership
@@ -4708,7 +4708,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership
@@ -6228,6 +6228,10 @@ Query: UPDATE groups SET user_member_profile_sent_at = ? WHERE user_id = ? AND g
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE groups SET via_group_link_uri_hash = ?, conn_link_prepared_connection = ?, updated_at = ? WHERE group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE note_folders SET chat_ts = ? WHERE user_id = ? AND note_folder_id = ?
Plan:
SEARCH note_folders USING INTEGER PRIMARY KEY (rowid=?)
@@ -145,7 +145,8 @@ CREATE TABLE groups(
conn_short_link_to_connect BLOB,
conn_link_started_connection INTEGER NOT NULL DEFAULT 0,
welcome_shared_msg_id BLOB,
request_shared_msg_id BLOB, -- received
request_shared_msg_id BLOB,
conn_link_prepared_connection INTEGER NOT NULL DEFAULT 0, -- received
FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE
+4 -19
View File
@@ -434,21 +434,6 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
|]
(userId, profileId, userId, profileId, userId, profileId)
deleteIncognitoConnectionProfile :: DB.Connection -> UserId -> Connection -> IO ()
deleteIncognitoConnectionProfile db userId Connection {connId, customUserProfileId} =
forM_ customUserProfileId $ \profileId -> do
DB.execute db "UPDATE connections SET custom_user_profile_id = NULL WHERE connection_id = ?" (Only connId)
DB.execute
db
[sql|
DELETE FROM contact_profiles
WHERE user_id = ? AND contact_profile_id = ?
AND NOT EXISTS (SELECT 1 FROM contacts WHERE contact_profile_id = ?)
AND NOT EXISTS (SELECT 1 FROM contact_requests WHERE contact_profile_id = ?)
AND NOT EXISTS (SELECT 1 FROM group_members WHERE contact_profile_id = ? OR member_profile_id = ?)
|]
(userId, profileId, profileId, profileId, profileId, profileId)
type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Maybe SharedMsgId, Maybe SharedMsgId)
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
@@ -623,7 +608,7 @@ safeDeleteLDN db User {userId} localDisplayName = do
|]
(userId, localDisplayName, userId)
type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt, Maybe SharedMsgId, Maybe SharedMsgId)
type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, Maybe SharedMsgId, Maybe SharedMsgId)
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
@@ -643,8 +628,8 @@ toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName,
toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup
toPreparedGroup = \case
(Just fullLink, shortLink_, BI connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId) ->
Just PreparedGroup {connLinkToConnect = CCLink fullLink shortLink_, connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId}
(Just fullLink, shortLink_, BI connLinkPreparedConnection, BI connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId) ->
Just PreparedGroup {connLinkToConnect = CCLink fullLink shortLink_, connLinkPreparedConnection, connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId}
_ -> Nothing
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
@@ -679,7 +664,7 @@ groupInfoQuery =
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership
+4 -3
View File
@@ -494,6 +494,7 @@ instance ToField BusinessChatType where toField = toField . textEncode
data PreparedGroup = PreparedGroup
{ connLinkToConnect :: CreatedLinkContact,
connLinkPreparedConnection :: Bool,
connLinkStartedConnection :: Bool,
welcomeSharedMsgId :: Maybe SharedMsgId, -- it is stored only for business chats, and only if welcome message is specified
requestSharedMsgId :: Maybe SharedMsgId
@@ -646,10 +647,10 @@ redactedMemberProfile Profile {displayName, fullName, image} =
data IncognitoProfile = NewIncognito Profile | ExistingIncognito LocalProfile
profileToSendOnAccept :: User -> Maybe IncognitoProfile -> Bool -> Profile
profileToSendOnAccept user ip = userProfileToSend user (getIncognitoProfile <$> ip) Nothing
userProfileToSend' :: User -> Maybe IncognitoProfile -> Maybe Contact -> Bool -> Profile
userProfileToSend' user ip = userProfileToSend user (fromIncognitoProfile <$> ip)
where
getIncognitoProfile = \case
fromIncognitoProfile = \case
NewIncognito p -> p
ExistingIncognito lp -> fromLocalProfile lp
+1
View File
@@ -386,6 +386,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
CEvtGroupMemberSwitch u g m progress -> ttyUser u $ viewGroupMemberSwitch g m progress
CEvtContactRatchetSync u ct progress -> ttyUser u $ viewContactRatchetSync ct progress
CEvtGroupMemberRatchetSync u g m progress -> ttyUser u $ viewGroupMemberRatchetSync g m progress
CEvtChatInfoUpdated u chatInfo -> []
CEvtNewChatItems u chatItems -> viewChatItems ttyUser unmuted u chatItems ts tz
CEvtChatItemsStatusesUpdated u chatItems
| length chatItems <= 20 ->