This commit is contained in:
Evgeny 2025-06-28 07:11:21 +00:00 committed by GitHub
commit 136856f477
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 367 additions and 224 deletions

View file

@ -1420,7 +1420,6 @@ struct UserContactLink: Decodable, Hashable {
struct AddressSettings: Codable, Hashable { struct AddressSettings: Codable, Hashable {
var businessAddress: Bool var businessAddress: Bool
var welcomeMessage: String?
var autoAccept: AutoAccept? var autoAccept: AutoAccept?
var autoReply: MsgContent? var autoReply: MsgContent?
} }

View file

@ -701,6 +701,7 @@ struct ComposeView: View {
private func sendMemberContactInvitation() { private func sendMemberContactInvitation() {
Task { Task {
do { do {
await MainActor.run { hideKeyboard() }
if let mc = connectCheckLinkPreview() { if let mc = connectCheckLinkPreview() {
await sending() await sending()
let contact = try await apiSendMemberContactInvitation(chat.chatInfo.apiId, mc) let contact = try await apiSendMemberContactInvitation(chat.chatInfo.apiId, mc)
@ -733,13 +734,14 @@ struct ComposeView: View {
), ),
secondaryButton: secondaryButton:
empty empty
? .cancel(Text("Add message")) { keyboardVisible = true } ? .cancel(Text("Add message"), action: hideKeyboard)
: .cancel() : .cancel()
)) ))
} }
private func sendConnectPreparedContact() { private func sendConnectPreparedContact() {
Task { Task {
await MainActor.run { hideKeyboard() }
await sending() await sending()
let mc = connectCheckLinkPreview() let mc = connectCheckLinkPreview()
if let contact = await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) { if let contact = await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) {
@ -756,6 +758,7 @@ struct ComposeView: View {
private func connectPreparedGroup() { private func connectPreparedGroup() {
Task { Task {
await MainActor.run { hideKeyboard() }
await sending() await sending()
let mc = connectCheckLinkPreview() let mc = connectCheckLinkPreview()
if let groupInfo = await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) { if let groupInfo = await apiConnectPreparedGroup(groupId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) {

View file

@ -371,7 +371,6 @@ struct ToggleShortLinkHeader: View {
struct AddressSettingsState: Equatable { struct AddressSettingsState: Equatable {
var businessAddress = false var businessAddress = false
var welcomeMessage = ""
var autoAccept = false var autoAccept = false
var autoAcceptIncognito = false var autoAcceptIncognito = false
var autoReply = "" var autoReply = ""
@ -380,7 +379,6 @@ struct AddressSettingsState: Equatable {
init(settings: AddressSettings) { init(settings: AddressSettings) {
self.businessAddress = settings.businessAddress self.businessAddress = settings.businessAddress
self.welcomeMessage = settings.welcomeMessage ?? ""
self.autoAccept = settings.autoAccept != nil self.autoAccept = settings.autoAccept != nil
self.autoAcceptIncognito = settings.autoAccept?.acceptIncognito == true self.autoAcceptIncognito = settings.autoAccept?.acceptIncognito == true
self.autoReply = settings.autoReply?.text ?? "" self.autoReply = settings.autoReply?.text ?? ""
@ -389,7 +387,6 @@ struct AddressSettingsState: Equatable {
var addressSettings: AddressSettings { var addressSettings: AddressSettings {
AddressSettings( AddressSettings(
businessAddress: self.businessAddress, businessAddress: self.businessAddress,
welcomeMessage: self.welcomeMessage.isEmpty ? nil : self.welcomeMessage,
autoAccept: self.autoAccept ? AutoAccept(acceptIncognito: self.autoAcceptIncognito) : nil, autoAccept: self.autoAccept ? AutoAccept(acceptIncognito: self.autoAcceptIncognito) : nil,
autoReply: self.autoReply.isEmpty ? nil : MsgContent.text(self.autoReply) autoReply: self.autoReply.isEmpty ? nil : MsgContent.text(self.autoReply)
) )
@ -463,14 +460,8 @@ struct UserAddressSettingsView: View {
autoAcceptToggle().disabled(settings.businessAddress) autoAcceptToggle().disabled(settings.businessAddress)
} }
Section { // TODO v6.4.1 move auto-reply editor here
messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.welcomeMessage) messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.autoReply)
} header: {
Text("Welcome message")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("Shown to your contact before connection.")
}
if settings.autoAccept { if settings.autoAccept {
autoAcceptSection() autoAcceptSection()
@ -554,7 +545,8 @@ struct UserAddressSettingsView: View {
if !ChatModel.shared.addressShortLinkDataSet && !settings.businessAddress { if !ChatModel.shared.addressShortLinkDataSet && !settings.businessAddress {
acceptIncognitoToggle() acceptIncognitoToggle()
} }
messageEditor(placeholder: NSLocalizedString("Enter auto-reply message… (optional)", comment: "placeholder"), text: $settings.autoReply) // TODO v6.4.1 show this message editor even with auto-accept disabled
messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.autoReply)
} header: { } header: {
Text("Auto-accept") Text("Auto-accept")
.foregroundColor(theme.colors.secondary) .foregroundColor(theme.colors.secondary)

View file

@ -2217,7 +2217,7 @@ public enum MemberCriteria: String, Codable, Identifiable, Hashable {
public struct ContactShortLinkData: Codable, Hashable { public struct ContactShortLinkData: Codable, Hashable {
public var profile: Profile public var profile: Profile
public var message: String? public var message: MsgContent?
public var business: Bool public var business: Bool
} }

View file

@ -521,7 +521,7 @@ object ChatModel {
} }
} }
} }
private fun chatItemBelongsToScope(cInfo: ChatInfo, cItem: ChatItem): Boolean = private fun chatItemBelongsToScope(cInfo: ChatInfo, cItem: ChatItem): Boolean =
when (secondaryContextFilter) { when (secondaryContextFilter) {
null -> null ->
@ -2063,7 +2063,7 @@ enum class MemberCriteria {
@Serializable @Serializable
data class ContactShortLinkData ( data class ContactShortLinkData (
val profile: Profile, val profile: Profile,
val message: String?, val message: MsgContent?,
val business: Boolean val business: Boolean
) )

View file

@ -64,7 +64,7 @@ initializeBotAddress' logAddress cc = do
when logAddress $ do when logAddress $ do
putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri) putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri)
when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri) when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri)
let settings = AddressSettings {businessAddress = False, welcomeMessage = Nothing, autoAccept = Just AutoAccept {acceptIncognito = False}, autoReply = Nothing} let settings = AddressSettings {businessAddress = False, autoAccept = Just AutoAccept {acceptIncognito = False}, autoReply = Nothing}
void $ sendChatCmd cc $ SetAddressSettings settings void $ sendChatCmd cc $ SetAddressSettings settings
sendMessage :: ChatController -> Contact -> Text -> IO () sendMessage :: ChatController -> Contact -> Text -> IO ()

View file

@ -72,6 +72,7 @@ import Simplex.Chat.Library.Internal
import Simplex.Chat.Stats import Simplex.Chat.Stats
import Simplex.Chat.Store import Simplex.Chat.Store
import Simplex.Chat.Store.AppSettings import Simplex.Chat.Store.AppSettings
import Simplex.Chat.Store.ContactRequest
import Simplex.Chat.Store.Connections import Simplex.Chat.Store.Connections
import Simplex.Chat.Store.Direct import Simplex.Chat.Store.Direct
import Simplex.Chat.Store.Files import Simplex.Chat.Store.Files
@ -1153,10 +1154,10 @@ processChatCommand' vr = \case
uclId <- getUserContactLinkIdByCReq db connReqId uclId <- getUserContactLinkIdByCReq db connReqId
uclGLinkInfo <- getUserContactLinkById db userId uclId uclGLinkInfo <- getUserContactLinkById db userId uclId
pure (uclId, uclGLinkInfo) pure (uclId, uclGLinkInfo)
let UserContactLink {shortLinkDataSet} = ucl let UserContactLink {shortLinkDataSet, addressSettings} = ucl
when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data" when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data"
withUserContactLock "acceptContact" uclId $ do withUserContactLock "acceptContact" uclId $ do
cReq <- withFastStore $ \db -> getContactRequest db user connReqId cReq@UserContactRequest {welcomeSharedMsgId} <- withFastStore $ \db -> getContactRequest db user connReqId
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito (ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
let contactUsed = isNothing gLinkInfo_ let contactUsed = isNothing gLinkInfo_
ct' <- withStore' $ \db -> do ct' <- withStore' $ \db -> do
@ -1166,6 +1167,13 @@ processChatCommand' vr = \case
then conn {connStatus = ConnSndReady} <$ updateConnectionStatusFromTo db connId ConnNew ConnSndReady then conn {connStatus = ConnSndReady} <$ updateConnectionStatusFromTo db connId ConnNew ConnSndReady
else pure conn else pure conn
pure ct {contactUsed, activeConn = Just 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' pure $ CRAcceptingContactRequest user ct'
APIRejectContact connReqId -> withUser $ \user -> do APIRejectContact connReqId -> withUser $ \user -> do
userContactLinkId <- withFastStore $ \db -> getUserContactLinkIdByCReq db connReqId userContactLinkId <- withFastStore $ \db -> getUserContactLinkIdByCReq db connReqId
@ -1748,7 +1756,7 @@ processChatCommand' vr = \case
createItem sharedMsgId content = createChatItem user cd True content sharedMsgId Nothing createItem sharedMsgId content = createChatItem user cd True content sharedMsgId Nothing
cInfo = GroupChat gInfo Nothing cInfo = GroupChat gInfo Nothing
void $ createGroupFeatureItems_ user cd True CIRcvGroupFeature gInfo void $ createGroupFeatureItems_ user cd True CIRcvGroupFeature gInfo
aci <- mapM (createItem welcomeSharedMsgId . CIRcvMsgContent . MCText) message aci <- mapM (createItem welcomeSharedMsgId . CIRcvMsgContent) message
let chat = case aci of let chat = case aci of
Just (AChatItem SCTGroup dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci} Just (AChatItem SCTGroup dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
_ -> Chat cInfo [] emptyChatStats _ -> Chat cInfo [] emptyChatStats
@ -1759,7 +1767,7 @@ processChatCommand' vr = \case
cInfo = DirectChat ct cInfo = DirectChat ct
void $ createItem Nothing $ CIRcvDirectE2EEInfo $ E2EInfo $ connRequestPQEncryption cReq void $ createItem Nothing $ CIRcvDirectE2EEInfo $ E2EInfo $ connRequestPQEncryption cReq
void $ createFeatureEnabledItems_ user ct void $ createFeatureEnabledItems_ user ct
aci <- mapM (createItem welcomeSharedMsgId . CIRcvMsgContent . MCText) message aci <- mapM (createItem welcomeSharedMsgId . CIRcvMsgContent) message
let chat = case aci of let chat = case aci of
Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci} Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
_ -> Chat cInfo [] emptyChatStats _ -> Chat cInfo [] emptyChatStats
@ -1804,9 +1812,13 @@ processChatCommand' vr = \case
ci <- saveSndChatItem user (CDDirectSnd ct') msg (CISndMsgContent mc) ci <- saveSndChatItem user (CDDirectSnd ct') msg (CISndMsgContent mc)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci] toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci]
pure $ CRStartedConnectionToContact user ct' customUserProfile pure $ CRStartedConnectionToContact user ct' customUserProfile
Just PreparedContact {connLinkToConnect = ACCL SCMContact ccLink, welcomeSharedMsgId} -> do Just PreparedContact {connLinkToConnect = ACCL SCMContact ccLink, welcomeSharedMsgId, requestSharedMsgId} -> do
-- TODO [short links] reuse welcomeSharedMsgId msg_ <- forM msgContent_ $ \mc -> case requestSharedMsgId of
msg_ <- forM msgContent_ $ \mc -> (,mc) <$> getSharedMsgId Just smId -> pure (smId, mc)
Nothing -> do
smId <- getSharedMsgId
withFastStore' $ \db -> setRequestSharedMsgIdForContact db contactId smId
pure (smId, mc)
connectViaContact user incognito ccLink welcomeSharedMsgId msg_ (Just $ ACCGContact contactId) >>= \case connectViaContact user incognito ccLink welcomeSharedMsgId msg_ (Just $ ACCGContact contactId) >>= \case
CRSentInvitation {customUserProfile} -> do CRSentInvitation {customUserProfile} -> do
-- get updated contact with connection -- get updated contact with connection
@ -1820,12 +1832,20 @@ processChatCommand' vr = \case
(gInfo, hostMember) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user groupId <*> getHostMember db vr user groupId (gInfo, hostMember) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user groupId <*> getHostMember db vr user groupId
case preparedGroup gInfo of case preparedGroup gInfo of
Nothing -> throwCmdError "group doesn't have link to connect" Nothing -> throwCmdError "group doesn't have link to connect"
Just PreparedGroup {connLinkToConnect} -> Just PreparedGroup {connLinkToConnect, welcomeSharedMsgId, requestSharedMsgId} -> do
-- TODO [short links] store request message with shared message ID (for business chat) msg_ <- forM msgContent_ $ \mc -> case requestSharedMsgId of
connectViaContact user incognito connLinkToConnect Nothing Nothing (Just $ ACCGGroup gInfo (groupMemberId' hostMember)) >>= \case Just smId -> pure (smId, mc)
Nothing -> do
smId <- getSharedMsgId
withFastStore' $ \db -> setRequestSharedMsgIdForGroup db groupId smId
pure (smId, mc)
connectViaContact user incognito connLinkToConnect welcomeSharedMsgId msg_ (Just $ ACCGGroup gInfo $ groupMemberId' hostMember) >>= \case
CRSentInvitation {customUserProfile} -> do CRSentInvitation {customUserProfile} -> do
-- get updated group info (connLinkStartedConnection and incognito membership) -- get updated group info (connLinkStartedConnection and incognito membership)
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
forM_ msg_ $ \(sharedMsgId, mc) -> do
ci <- createChatItem user (CDGroupSnd gInfo' Nothing) False (CISndMsgContent mc) (Just sharedMsgId) Nothing
toView $ CEvtNewChatItems user [ci]
pure $ CRStartedConnectionToGroup user gInfo' customUserProfile pure $ CRStartedConnectionToGroup user gInfo' customUserProfile
cr -> pure cr cr -> pure cr
APIConnect userId incognito acl -> withUserId userId $ \user -> case acl of APIConnect userId incognito acl -> withUserId userId $ \user -> case acl of
@ -3461,7 +3481,7 @@ processChatCommand' vr = \case
contactShortLinkData :: Profile -> Maybe AddressSettings -> CM UserLinkData contactShortLinkData :: Profile -> Maybe AddressSettings -> CM UserLinkData
contactShortLinkData p settings = do contactShortLinkData p settings = do
large <- chatReadVar useLargeLinkData large <- chatReadVar useLargeLinkData
let msg = welcomeMessage =<< settings let msg = autoReply =<< settings
business = maybe False businessAddress settings business = maybe False businessAddress settings
contactData contactData
| large = ContactShortLinkData p msg business | large = ContactShortLinkData p msg business
@ -4769,10 +4789,10 @@ chatCommandP =
dbEncryptionConfig currentKey newKey = DBEncryptionConfig {currentKey, newKey, keepKey = Just False} dbEncryptionConfig currentKey newKey = DBEncryptionConfig {currentKey, newKey, keepKey = Just False}
#endif #endif
-- TODO [short links] parser for address settings -- TODO [short links] parser for address settings
autoAcceptP = ifM onOffP (businessAA <|> addressAA) (pure $ AddressSettings False Nothing Nothing Nothing) autoAcceptP = ifM onOffP (businessAA <|> addressAA) (pure $ AddressSettings False Nothing Nothing)
where where
addressAA = AddressSettings False Nothing <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> autoReply addressAA = AddressSettings False <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> autoReply
businessAA = " business" *> (AddressSettings True Nothing (Just $ AutoAccept False) <$> autoReply) businessAA = " business" *> (AddressSettings True (Just $ AutoAccept False) <$> autoReply)
autoReply = optional (A.space *> msgContentP) autoReply = optional (A.space *> msgContentP)
rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P)) rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P))
text1P = safeDecodeUtf8 <$> A.takeTill (== ' ') text1P = safeDecodeUtf8 <$> A.takeTill (== ' ')

View file

@ -36,7 +36,7 @@ import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text.Encoding (decodeLatin1) import Data.Text.Encoding (decodeLatin1)
import Data.Time.Clock (UTCTime, diffUTCTime) import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime)
import qualified Data.UUID as UUID import qualified Data.UUID as UUID
import qualified Data.UUID.V4 as V4 import qualified Data.UUID.V4 as V4
import Data.Word (Word32) import Data.Word (Word32)
@ -580,9 +580,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
createFeatureEnabledItems user ct' createFeatureEnabledItems user ct'
(Just PreparedContact {connLinkToConnect = ACCL _ (CCLink cReq _)}, _) -> (Just PreparedContact {connLinkToConnect = ACCL _ (CCLink cReq _)}, _) ->
unless (Just pqEnc == connRequestPQEncryption cReq) createE2EItem unless (Just pqEnc == connRequestPQEncryption cReq) createE2EItem
(_, Just connReqId) -> do (_, Just connReqId) ->
UserContactRequest {pqSupport} <- withStore $ \db -> getContactRequest db user connReqId withStore' (\db -> getContactRequest' db user connReqId) >>= \case
unless (CR.pqSupportToEnc pqSupport == pqEnc) createE2EItem Just UserContactRequest {pqSupport} | CR.pqSupportToEnc pqSupport == pqEnc -> pure ()
_ -> createE2EItem
when (contactConnInitiated conn') $ do when (contactConnInitiated conn') $ do
let Connection {groupLinkId} = conn' let Connection {groupLinkId} = conn'
doProbeContacts = isJust groupLinkId doProbeContacts = isJust groupLinkId
@ -590,7 +591,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
withStore' $ \db -> resetContactConnInitiated db user conn' withStore' $ \db -> resetContactConnInitiated db user conn'
forM_ viaUserContactLink $ \userContactLinkId -> do forM_ viaUserContactLink $ \userContactLinkId -> do
(ucl, gli_) <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId (ucl, gli_) <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId
when (connChatVersion < batchSend2Version) $ sendAutoReply ucl ct' -- let UserContactLink {addressSettings = AddressSettings {autoReply}} = ucl
when (connChatVersion < batchSend2Version) $ forM_ (autoReply $ addressSettings ucl) $ \mc -> sendAutoReply ct' mc Nothing -- old versions only
-- TODO REMOVE LEGACY vvv -- TODO REMOVE LEGACY vvv
forM_ gli_ $ \GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do forM_ gli_ $ \GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do
groupInfo <- withStore $ \db -> getGroupInfo db vr user groupId groupInfo <- withStore $ \db -> getGroupInfo db vr user groupId
@ -656,9 +658,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
when (directOrUsed ct && sqSecured) $ do when (directOrUsed ct && sqSecured) $ do
lift $ setContactNetworkStatus ct NSConnected lift $ setContactNetworkStatus ct NSConnected
toView $ CEvtContactSndReady user ct toView $ CEvtContactSndReady user ct
forM_ viaUserContactLink $ \userContactLinkId -> do when (connChatVersion >= batchSend2Version) $ forM_ viaUserContactLink $ \userContactLinkId -> do
(ucl, _) <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId (ucl, _) <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId
when (connChatVersion >= batchSend2Version) $ sendAutoReply ucl ct forM_ (autoReply $ addressSettings ucl) $ \mc -> do
connReq_ <- pure (contactRequestId' ct) $>>= \connReqId -> withStore' (\db -> getContactRequest' db user connReqId)
sendAutoReply ct mc connReq_
QCONT -> QCONT ->
void $ continueSending connEntity conn void $ continueSending connEntity conn
MWARN msgId err -> do MWARN msgId err -> do
@ -678,9 +682,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- TODO add debugging output -- TODO add debugging output
_ -> pure () _ -> pure ()
where where
sendAutoReply UserContactLink {addressSettings = AddressSettings {autoReply}} ct = sendAutoReply ct mc = \case
forM_ autoReply $ \mc -> do Just UserContactRequest {welcomeSharedMsgId = Just smId} ->
(msg, _) <- sendDirectContactMessage user ct (XMsgNew $ MCSimple (extMsgContent mc Nothing)) void $ sendDirectContactMessage user ct $ XMsgUpdate smId mc M.empty Nothing Nothing Nothing
_ -> do
(msg, _) <- sendDirectContactMessage user ct $ XMsgNew $ MCSimple $ extMsgContent mc Nothing
ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc) ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci] toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
@ -808,9 +814,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
(gInfo'', m'', scopeInfo) <- mkGroupChatScope gInfo' m' (gInfo'', m'', scopeInfo) <- mkGroupChatScope gInfo' m'
let cd = CDGroupRcv gInfo'' scopeInfo m'' let cd = CDGroupRcv gInfo'' scopeInfo m''
createInternalChatItem user cd (CIRcvGroupE2EEInfo E2EInfo {pqEnabled = Just PQEncOff}) Nothing createInternalChatItem user cd (CIRcvGroupE2EEInfo E2EInfo {pqEnabled = Just PQEncOff}) Nothing
createGroupFeatureItems user cd CIRcvGroupFeature gInfo'' let business = isJust $ businessChat gInfo''
unless business $ createGroupFeatureItems user cd CIRcvGroupFeature gInfo''
memberConnectedChatItem gInfo'' scopeInfo m'' memberConnectedChatItem gInfo'' scopeInfo m''
unless (memberPending membership) $ maybeCreateGroupDescrLocal gInfo'' m'' unless (memberPending membership || business) $ maybeCreateGroupDescrLocal gInfo'' m''
GCInviteeMember -> do GCInviteeMember -> do
(gInfo', mStatus) <- (gInfo', mStatus) <-
if not (memberPending m) if not (memberPending m)
@ -829,7 +836,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus} toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus}
let Connection {viaUserContactLink} = conn let Connection {viaUserContactLink} = conn
when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo'' when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo''
when (connChatVersion < batchSend2Version) sendGroupAutoReply when (connChatVersion < batchSend2Version) $ getAutoReplyMsg >>= mapM_ (\mc -> sendGroupAutoReply mc Nothing)
case mStatus of case mStatus of
GSMemPendingApproval -> pure () GSMemPendingApproval -> pure ()
GSMemPendingReview -> introduceToModerators vr user gInfo'' m' GSMemPendingReview -> introduceToModerators vr user gInfo'' m'
@ -1011,7 +1018,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
JOINED sqSecured _serviceId -> JOINED sqSecured _serviceId ->
-- [async agent commands] continuation on receiving JOINED -- [async agent commands] continuation on receiving JOINED
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData ->
when (sqSecured && connChatVersion >= batchSend2Version) sendGroupAutoReply when (sqSecured && connChatVersion >= batchSend2Version) $ do
mc_ <- getAutoReplyMsg
forM_ mc_ $ \mc -> do
connReq_ <- withStore' $ \db -> getBusinessContactRequest db user groupId
sendGroupAutoReply mc connReq_
QCONT -> do QCONT -> do
continued <- continueSending connEntity conn continued <- continueSending connEntity conn
when continued $ sendPendingGroupMessages user m conn when continued $ sendPendingGroupMessages user m conn
@ -1039,22 +1050,23 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
updateGroupItemsErrorStatus db msgId groupMemberId newStatus = do updateGroupItemsErrorStatus db msgId groupMemberId newStatus = do
itemIds <- getChatItemIdsByAgentMsgId db connId msgId itemIds <- getChatItemIdsByAgentMsgId db connId msgId
forM_ itemIds $ \itemId -> updateGroupMemSndStatus' db itemId groupMemberId newStatus forM_ itemIds $ \itemId -> updateGroupMemSndStatus' db itemId groupMemberId newStatus
sendGroupAutoReply = autoReplyMC >>= mapM_ send getAutoReplyMsg = do
where let GroupInfo {businessChat} = gInfo
autoReplyMC = do GroupMember {memberId = joiningMemberId} = m
let GroupInfo {businessChat} = gInfo case businessChat of
GroupMember {memberId = joiningMemberId} = m Just BusinessChatInfo {customerId, chatType = BCCustomer}
case businessChat of | joiningMemberId == customerId -> useReply <$> withStore (`getUserAddress` user)
Just BusinessChatInfo {customerId, chatType = BCCustomer} where
| joiningMemberId == customerId -> useReply <$> withStore (`getUserAddress` user) useReply UserContactLink {addressSettings = AddressSettings {autoReply}} = autoReply
where _ -> pure Nothing
useReply UserContactLink {addressSettings = AddressSettings {autoReply}} = autoReply sendGroupAutoReply mc = \case
_ -> pure Nothing Just UserContactRequest {welcomeSharedMsgId = Just smId} ->
send mc = do void $ sendGroupMessage' user gInfo [m] $ XMsgUpdate smId mc M.empty Nothing Nothing Nothing
msg <- sendGroupMessage' user gInfo [m] (XMsgNew $ MCSimple (extMsgContent mc Nothing)) _ -> do
ci <- saveSndChatItem user (CDGroupSnd gInfo Nothing) msg (CISndMsgContent mc) msg <- sendGroupMessage' user gInfo [m] $ XMsgNew $ MCSimple $ extMsgContent mc Nothing
withStore' $ \db -> createGroupSndStatus db (chatItemId' ci) (groupMemberId' m) GSSNew ci <- saveSndChatItem user (CDGroupSnd gInfo Nothing) msg (CISndMsgContent mc)
toView $ CEvtNewChatItems user [AChatItem SCTGroup SMDSnd (GroupChat gInfo Nothing) ci] withStore' $ \db -> createGroupSndStatus db (chatItemId' ci) (groupMemberId' m) GSSNew
toView $ CEvtNewChatItems user [AChatItem SCTGroup SMDSnd (GroupChat gInfo Nothing) ci]
agentMsgDecryptError :: AgentCryptoError -> (MsgDecryptError, Word32) agentMsgDecryptError :: AgentCryptoError -> (MsgDecryptError, Word32)
agentMsgDecryptError = \case agentMsgDecryptError = \case
@ -1255,56 +1267,121 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
REBusinessChat gInfo _clientMember -> REBusinessChat gInfo _clientMember ->
-- TODO [short links] update request msg -- TODO [short links] update request msg
toView $ CEvtBusinessRequestAlreadyAccepted user gInfo toView $ CEvtBusinessRequestAlreadyAccepted user gInfo
RSCurrentRequest ucr re_ repeatRequest -> case re_ of RSCurrentRequest prevUcr_ ucr@UserContactRequest {welcomeSharedMsgId} re_ -> case re_ of
Nothing -> toView $ CEvtReceivedContactRequest user ucr Nothing Nothing -> toView $ CEvtReceivedContactRequest user ucr Nothing
Just (REContact ct) -> do Just (REContact ct) -> do
-- TODO [short links] prevent duplicate items let cd = CDDirectRcv ct
-- update welcome message if changed (send update event to UI) and add updated feature items. aci_ <- case prevUcr_ of
-- Do not created e2e item on repeat request Just UserContactRequest {requestSharedMsgId = prevSharedMsgId_} ->
if repeatRequest -- TODO [short links] this branch does not update feature items and e2e items, as they are highly unlikely to change
then do -- they will be updated after connection is accepted.
-- TODO [short links] update request msg upsertDirectRequestItem cd (requestMsg_, prevSharedMsgId_)
-- .... Nothing -> do
acceptOrShow Nothing -- pass item? let e2eContent = CIRcvDirectE2EEInfo $ E2EInfo $ Just $ CR.pqSupportToEnc $ reqPQSup
else do void $ createChatItem user cd False e2eContent Nothing Nothing
-- TODO [short links] save sharedMsgId instead of the last Nothing
let createItem content = createChatItem user (CDDirectRcv ct) False content Nothing Nothing
void $ createItem $ CIRcvDirectE2EEInfo $ E2EInfo $ Just $ CR.pqSupportToEnc $ reqPQSup
void $ createFeatureEnabledItems_ user ct void $ createFeatureEnabledItems_ user ct
-- TODO [short links] save sharedMsgId forM_ (autoReply addressSettings) $ \mc -> forM_ welcomeSharedMsgId $ \sharedMsgId ->
aci <- forM requestMsg_ $ \(sharedMsgId, mc) -> do createChatItem user (CDDirectSnd ct) False (CISndMsgContent mc) (Just sharedMsgId) Nothing
aci <- createItem $ CIRcvMsgContent mc mapM (createRequestItem cd) requestMsg_
unlessM (asks $ coreApi . config) $ toView $ CEvtNewChatItems user [aci] case autoAccept of
pure aci Nothing -> do
acceptOrShow aci let cInfo = DirectChat ct
where chat = AChat SCTDirect $ case aci_ of
acceptOrShow aci_ = Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
case autoAccept of _ -> Chat cInfo [] emptyChatStats
Nothing -> do toView $ CEvtReceivedContactRequest user ucr $ Just chat
let cInfo = DirectChat ct Just AutoAccept {acceptIncognito} -> do
chat = AChat SCTDirect $ case aci_ of incognitoProfile <-
Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci} if not shortLinkDataSet && acceptIncognito
_ -> Chat cInfo [] emptyChatStats then Just . NewIncognito <$> liftIO generateRandomProfile
toView $ CEvtReceivedContactRequest user ucr (Just chat) else pure Nothing
Just AutoAccept {acceptIncognito} -> do ct' <- acceptContactRequestAsync user uclId ct ucr incognitoProfile
incognitoProfile <- toView $ CEvtAcceptingContactRequest user ct'
if not shortLinkDataSet && acceptIncognito
then Just . NewIncognito <$> liftIO generateRandomProfile
else pure Nothing
ct' <- acceptContactRequestAsync user uclId ct ucr incognitoProfile
-- chat in event?
toView $ CEvtAcceptingContactRequest user ct'
Just (REBusinessChat gInfo clientMember) -> do Just (REBusinessChat gInfo clientMember) -> do
-- TODO [short links] prevent duplicate items (use repeatRequest like for REContact)
(_gInfo', _clientMember') <- acceptBusinessJoinRequestAsync user uclId gInfo clientMember ucr (_gInfo', _clientMember') <- acceptBusinessJoinRequestAsync user uclId gInfo clientMember ucr
-- TODO [short links] add welcome message if welcomeMsgId is present let cd = CDGroupRcv gInfo Nothing clientMember
-- forM_ autoReply $ \arMC -> void $ case prevUcr_ of
-- when (shortLinkDataSet && v >= shortLinkDataVersion) $ Just UserContactRequest {requestSharedMsgId = prevSharedMsgId_} ->
-- createInternalChatItem user (CDGroupSnd gInfo Nothing) (CISndMsgContent arMC) Nothing -- TODO [short links] this branch does not update feature items and e2e items, as they are highly unlikely to change
-- TODO [short links] save sharedMsgId -- they will be updated after connection is accepted.
forM_ requestMsg_ $ \(sharedMsgId, mc) -> upsertBusinessRequestItem cd (requestMsg_, prevSharedMsgId_)
createInternalChatItem user (CDGroupRcv gInfo Nothing clientMember) (CIRcvMsgContent mc) Nothing Nothing -> do
-- TODO [short links] possibly, we can just keep them created where they are created on the business side due to auto-accept
-- let e2eContent = CIRcvGroupE2EEInfo $ E2EInfo $ Just False -- no PQ encryption in groups
-- void $ createChatItem user cd False e2eContent Nothing Nothing
-- void $ createFeatureEnabledItems_ user ct
forM_ (autoReply addressSettings) $ \arMC -> forM_ welcomeSharedMsgId $ \sharedMsgId ->
createChatItem user (CDGroupSnd gInfo Nothing) False (CISndMsgContent arMC) (Just sharedMsgId) Nothing
mapM (createRequestItem cd) requestMsg_
toView $ CEvtAcceptingBusinessRequest user gInfo toView $ CEvtAcceptingBusinessRequest user gInfo
where
upsertDirectRequestItem :: ChatDirection 'CTDirect 'MDRcv -> (Maybe (SharedMsgId, MsgContent), Maybe SharedMsgId) -> CM (Maybe AChatItem)
upsertDirectRequestItem cd@(CDDirectRcv ct@Contact {contactId}) = upsertRequestItem cd updateRequestItem markRequestItemDeleted
where
updateRequestItem (sharedMsgId, mc) =
withStore (\db -> getDirectChatItemBySharedMsgId db user contactId sharedMsgId) >>= \case
CChatItem SMDRcv ci@ChatItem {content = CIRcvMsgContent oldMC}
| mc /= oldMC -> do
currentTs <- liftIO getCurrentTime
aci <- withStore' $ \db -> do
addInitialAndNewCIVersions db (chatItemId' ci) (chatItemTs' ci, oldMC) (currentTs, mc)
aChatItem <$> updateDirectChatItem' db user contactId ci (CIRcvMsgContent mc) True False Nothing Nothing
toView $ CEvtChatItemUpdated user aci
pure $ Just aci
| otherwise -> pure $ Just $ aChatItem ci
_ -> pure Nothing
where
aChatItem = AChatItem SCTDirect SMDRcv (DirectChat ct)
markRequestItemDeleted sharedMsgId =
withStore' (\db -> runExceptT $ getDirectChatItemBySharedMsgId db user contactId sharedMsgId) >>= \case
Right (cci@(CChatItem SMDRcv _)) -> do
currentTs <- liftIO getCurrentTime
deletions <- if featureAllowed SCFFullDelete forContact ct
then deleteDirectCIs user ct [cci]
else markDirectCIsDeleted user ct [cci] currentTs
toView $ CEvtChatItemsDeleted user deletions False False
_ -> pure ()
upsertBusinessRequestItem :: ChatDirection 'CTGroup 'MDRcv -> (Maybe (SharedMsgId, MsgContent), Maybe SharedMsgId) -> CM (Maybe AChatItem)
upsertBusinessRequestItem cd@(CDGroupRcv gInfo@GroupInfo {groupId} _ clientMember) = upsertRequestItem cd updateRequestItem markRequestItemDeleted
where
updateRequestItem (sharedMsgId, mc) =
withStore (\db -> getGroupChatItemBySharedMsgId db user gInfo (groupMemberId' clientMember) sharedMsgId) >>= \case
CChatItem SMDRcv ci@ChatItem {chatDir = CIGroupRcv m', content = CIRcvMsgContent oldMC}
| sameMemberId (memberId' clientMember) m' ->
if mc /= oldMC
then do
currentTs <- liftIO getCurrentTime
aci <- withStore' $ \db -> do
addInitialAndNewCIVersions db (chatItemId' ci) (chatItemTs' ci, oldMC) (currentTs, mc)
aChatItem <$> updateGroupChatItem db user groupId ci (CIRcvMsgContent mc) True False Nothing
toView $ CEvtChatItemUpdated user aci
pure $ Just aci
else pure $ Just $ aChatItem ci
_ -> pure Nothing
where
aChatItem = AChatItem SCTGroup SMDRcv (GroupChat gInfo Nothing)
markRequestItemDeleted sharedMsgId =
withStore' (\db -> runExceptT $ getGroupMemberCIBySharedMsgId db user gInfo (memberId' clientMember) sharedMsgId) >>= \case
Right cci@(CChatItem SMDRcv ChatItem {chatDir = CIGroupRcv m'})
| sameMemberId (memberId' clientMember) m' -> do
currentTs <- liftIO getCurrentTime
deletions <- if groupFeatureMemberAllowed SGFFullDelete clientMember gInfo
then deleteGroupCIs user gInfo Nothing [cci] Nothing currentTs
else markGroupCIsDeleted user gInfo Nothing [cci] Nothing currentTs
toView $ CEvtChatItemsDeleted user deletions False False
_ -> pure ()
createRequestItem :: ChatTypeI c => ChatDirection c 'MDRcv -> (SharedMsgId, MsgContent) -> CM AChatItem
createRequestItem cd (sharedMsgId, mc) = do
aci <- createChatItem user cd False (CIRcvMsgContent mc) (Just sharedMsgId) Nothing
toView $ CEvtNewChatItems user [aci]
pure aci
upsertRequestItem :: ChatTypeI c => ChatDirection c 'MDRcv -> ((SharedMsgId, MsgContent) -> CM (Maybe AChatItem)) -> (SharedMsgId -> CM ()) -> (Maybe (SharedMsgId, MsgContent), Maybe SharedMsgId) -> CM (Maybe AChatItem)
upsertRequestItem cd update delete = \case
(Just msg, Nothing) -> Just <$> createRequestItem cd msg
(Just msg@(sharedMsgId, _), Just prevSharedMsgId) | sharedMsgId == prevSharedMsgId ->
update msg `catchCINotFound` \_ -> Just <$> createRequestItem cd msg
(Nothing, Just prevSharedMsgId) -> Nothing <$ delete prevSharedMsgId
_ -> pure Nothing
-- ##### Group link join requests (don't create contact requests) ##### -- ##### Group link join requests (don't create contact requests) #####
Just gli@GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do Just gli@GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do
-- TODO [short links] deduplicate request by xContactId? -- TODO [short links] deduplicate request by xContactId?

View file

@ -1224,3 +1224,19 @@ instance ToJSON (ChatMessage 'Json) where
instance FromJSON (ChatMessage 'Json) where instance FromJSON (ChatMessage 'Json) where
parseJSON v = appJsonToCM <$?> parseJSON v parseJSON v = appJsonToCM <$?> parseJSON v
data ContactShortLinkData = ContactShortLinkData
{ profile :: Profile,
message :: Maybe MsgContent,
business :: Bool
}
deriving (Show)
data GroupShortLinkData = GroupShortLinkData
{ groupProfile :: GroupProfile
}
deriving (Show)
$(JQ.deriveJSON defaultJSON ''ContactShortLinkData)
$(JQ.deriveJSON defaultJSON ''GroupShortLinkData)

View file

@ -112,7 +112,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
[sql| [sql|
SELECT SELECT
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite, c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.contact_request_id, p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl
FROM contacts c FROM contacts c
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
@ -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.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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership} -- GroupInfo {membership}

View file

@ -12,7 +12,9 @@
module Simplex.Chat.Store.ContactRequest module Simplex.Chat.Store.ContactRequest
( createOrUpdateContactRequest, ( createOrUpdateContactRequest,
setContactAcceptedXContactId, setContactAcceptedXContactId,
setBusinessChatAcceptedXContactId setBusinessChatAcceptedXContactId,
setRequestSharedMsgIdForContact,
setRequestSharedMsgIdForGroup
) )
where where
@ -81,11 +83,14 @@ createOrUpdateContactRequest
Just xContactId -> Just xContactId ->
-- 1) first we try to find accepted contact or business chat by xContactId -- 1) first we try to find accepted contact or business chat by xContactId
liftIO (getAcceptedContact xContactId) >>= \case liftIO (getAcceptedContact xContactId) >>= \case
Just ct -> pure $ RSAcceptedRequest Nothing (REContact ct) Just ct -> do
cr <- liftIO $ getContactRequestByXContactId xContactId
pure $ RSAcceptedRequest cr (REContact ct)
Nothing -> liftIO (getAcceptedBusinessChat xContactId) >>= \case Nothing -> liftIO (getAcceptedBusinessChat xContactId) >>= \case
Just gInfo@GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do Just gInfo@GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do
clientMember <- getGroupMemberByMemberId db vr user gInfo customerId clientMember <- getGroupMemberByMemberId db vr user gInfo customerId
pure $ RSAcceptedRequest Nothing (REBusinessChat gInfo clientMember) cr <- liftIO $ getContactRequestByXContactId xContactId
pure $ RSAcceptedRequest cr (REBusinessChat gInfo clientMember)
Just GroupInfo {businessChat = Nothing} -> throwError SEInvalidBusinessChatContactRequest Just GroupInfo {businessChat = Nothing} -> throwError SEInvalidBusinessChatContactRequest
-- 2) if no legacy accepted contact or business chat was found, next we try to find an existing request -- 2) if no legacy accepted contact or business chat was found, next we try to find an existing request
Nothing -> Nothing ->
@ -105,7 +110,7 @@ createOrUpdateContactRequest
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
@ -196,7 +201,7 @@ createOrUpdateContactRequest
(contactId, contactRequestId) (contactId, contactRequestId)
ucr <- getContactRequest db user contactRequestId ucr <- getContactRequest db user contactRequestId
ct <- getContact db vr user contactId ct <- getContact db vr user contactId
pure $ RSCurrentRequest ucr (Just $ REContact ct) False pure $ RSCurrentRequest Nothing ucr (Just $ REContact ct)
createBusinessChat = do createBusinessChat = do
let Profile {preferences = userPreferences} = profileToSendOnAccept user Nothing True let Profile {preferences = userPreferences} = profileToSendOnAccept user Nothing True
groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs userPreferences groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs userPreferences
@ -208,15 +213,15 @@ createOrUpdateContactRequest
"UPDATE contact_requests SET business_group_id = ? WHERE contact_request_id = ?" "UPDATE contact_requests SET business_group_id = ? WHERE contact_request_id = ?"
(groupId, contactRequestId) (groupId, contactRequestId)
ucr <- getContactRequest db user contactRequestId ucr <- getContactRequest db user contactRequestId
pure $ RSCurrentRequest ucr (Just $ REBusinessChat gInfo clientMember) False pure $ RSCurrentRequest Nothing ucr (Just $ REBusinessChat gInfo clientMember)
updateContactRequest :: UserContactRequest -> ExceptT StoreError IO RequestStage updateContactRequest :: UserContactRequest -> ExceptT StoreError IO RequestStage
updateContactRequest UserContactRequest {contactRequestId, contactId_, localDisplayName = oldLdn, profile = Profile {displayName = oldDisplayName}} = do updateContactRequest ucr@UserContactRequest {contactRequestId, contactId_, localDisplayName = oldLdn, profile = Profile {displayName = oldDisplayName}} = do
currentTs <- liftIO getCurrentTime currentTs <- liftIO getCurrentTime
liftIO $ updateProfile currentTs liftIO $ updateProfile currentTs
updateRequest currentTs updateRequest currentTs
ucr' <- getContactRequest db user contactRequestId ucr' <- getContactRequest db user contactRequestId
re_ <- getRequestEntity ucr' re_ <- getRequestEntity ucr'
pure $ RSCurrentRequest ucr' re_ True pure $ RSCurrentRequest (Just ucr) ucr' re_
where where
updateProfile currentTs = updateProfile currentTs =
DB.execute DB.execute
@ -288,12 +293,16 @@ createOrUpdateContactRequest
setContactAcceptedXContactId :: DB.Connection -> Contact -> XContactId -> IO () setContactAcceptedXContactId :: DB.Connection -> Contact -> XContactId -> IO ()
setContactAcceptedXContactId db Contact {contactId} xContactId = setContactAcceptedXContactId db Contact {contactId} xContactId =
DB.execute DB.execute db "UPDATE contacts SET xcontact_id = ? WHERE contact_id = ?" (xContactId, contactId)
db "UPDATE contacts SET xcontact_id = ? WHERE contact_id = ?"
(xContactId, contactId)
setBusinessChatAcceptedXContactId :: DB.Connection -> GroupInfo -> XContactId -> IO () setBusinessChatAcceptedXContactId :: DB.Connection -> GroupInfo -> XContactId -> IO ()
setBusinessChatAcceptedXContactId db GroupInfo {groupId} xContactId = setBusinessChatAcceptedXContactId db GroupInfo {groupId} xContactId =
DB.execute DB.execute db "UPDATE groups SET business_xcontact_id = ? WHERE group_id = ?" (xContactId, groupId)
db "UPDATE groups SET business_xcontact_id = ? WHERE group_id = ?"
(xContactId, groupId) setRequestSharedMsgIdForContact :: DB.Connection -> ContactId -> SharedMsgId -> IO ()
setRequestSharedMsgIdForContact db contactId sharedMsgId = do
DB.execute db "UPDATE contacts SET request_shared_msg_id = ? WHERE contact_id = ?" (sharedMsgId, contactId)
setRequestSharedMsgIdForGroup :: DB.Connection -> GroupId -> SharedMsgId -> IO ()
setRequestSharedMsgIdForGroup db groupId sharedMsgId = do
DB.execute db "UPDATE groups SET request_shared_msg_id = ? WHERE group_id = ?" (sharedMsgId, groupId)

View file

@ -60,6 +60,8 @@ module Simplex.Chat.Store.Direct
getUserContacts, getUserContacts,
getUserContactLinkIdByCReq, getUserContactLinkIdByCReq,
getContactRequest, getContactRequest,
getContactRequest',
getBusinessContactRequest,
getContactRequestIdByName, getContactRequestIdByName,
deleteContactRequest, deleteContactRequest,
createContactFromRequest, createContactFromRequest,
@ -108,10 +110,10 @@ import qualified Simplex.Messaging.Agent.Store.DB as DB
import Simplex.Messaging.Crypto.Ratchet (PQSupport) import Simplex.Messaging.Crypto.Ratchet (PQSupport)
import Simplex.Messaging.Protocol (SubscriptionMode (..)) import Simplex.Messaging.Protocol (SubscriptionMode (..))
#if defined(dbPostgres) #if defined(dbPostgres)
import Database.PostgreSQL.Simple (Only (..), (:.) (..)) import Database.PostgreSQL.Simple (Only (..), Query, (:.) (..))
import Database.PostgreSQL.Simple.SqlQQ (sql) import Database.PostgreSQL.Simple.SqlQQ (sql)
#else #else
import Database.SQLite.Simple (Only (..), (:.) (..)) import Database.SQLite.Simple (Only (..), Query, (:.) (..))
import Database.SQLite.Simple.QQ (sql) import Database.SQLite.Simple.QQ (sql)
#endif #endif
@ -218,7 +220,7 @@ getContactByConnReqHash db vr user@User {userId} cReqHash = do
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
@ -286,7 +288,7 @@ createPreparedContact db user@User {userId} p@Profile {preferences} connLinkToCo
createdAt = currentTs, createdAt = currentTs,
updatedAt = currentTs, updatedAt = currentTs,
chatTs = Just currentTs, chatTs = Just currentTs,
preparedContact = Just $ PreparedContact connLinkToConnect (connMode m) welcomeSharedMsgId, preparedContact = Just PreparedContact {connLinkToConnect, uiConnLinkType = connMode m, welcomeSharedMsgId, requestSharedMsgId = Nothing},
contactRequestId = Nothing, contactRequestId = Nothing,
contactGroupMemberId = Nothing, contactGroupMemberId = Nothing,
contactGrpInvSent = False, contactGrpInvSent = False,
@ -687,23 +689,32 @@ getUserContactLinkIdByCReq db contactRequestId =
getContactRequest :: DB.Connection -> User -> Int64 -> ExceptT StoreError IO UserContactRequest getContactRequest :: DB.Connection -> User -> Int64 -> ExceptT StoreError IO UserContactRequest
getContactRequest db User {userId} contactRequestId = getContactRequest db User {userId} contactRequestId =
ExceptT . firstRow toContactRequest (SEContactRequestNotFound contactRequestId) $ ExceptT . firstRow toContactRequest (SEContactRequestNotFound contactRequestId) $
DB.query DB.query db (contactRequestQuery <> " WHERE cr.user_id = ? AND cr.contact_request_id = ?") (userId, contactRequestId)
db
[sql| getContactRequest' :: DB.Connection -> User -> Int64 -> IO (Maybe UserContactRequest)
SELECT getContactRequest' db User {userId} contactRequestId =
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id, maybeFirstRow toContactRequest $
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, DB.query db (contactRequestQuery <> " WHERE cr.user_id = ? AND cr.contact_request_id = ?") (userId, contactRequestId)
c.agent_conn_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, getBusinessContactRequest :: DB.Connection -> User -> GroupId -> IO (Maybe UserContactRequest)
cr.created_at, cr.updated_at, getBusinessContactRequest db User {userId} groupId =
cr.peer_chat_min_version, cr.peer_chat_max_version maybeFirstRow toContactRequest $
FROM contact_requests cr DB.query db (contactRequestQuery <> " WHERE cr.user_id = ? AND cr.business_group_id = ?") (userId, groupId)
JOIN connections c USING (user_contact_link_id)
JOIN contact_profiles p USING (contact_profile_id) contactRequestQuery :: Query
WHERE cr.user_id = ? contactRequestQuery =
AND cr.contact_request_id = ? [sql|
|] SELECT
(userId, contactRequestId) 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.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)
|]
getContactRequestIdByName :: DB.Connection -> UserId -> ContactName -> ExceptT StoreError IO Int64 getContactRequestIdByName :: DB.Connection -> UserId -> ContactName -> ExceptT StoreError IO Int64
getContactRequestIdByName db userId cName = getContactRequestIdByName db userId cName =
@ -807,7 +818,7 @@ getContact_ db vr user@User {userId} contactId deleted = do
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,

View file

@ -937,7 +937,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.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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, 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, 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,
@ -1291,7 +1291,7 @@ createBusinessRequestGroup
gVar gVar
user@User {userId, userContactId} user@User {userId, userContactId}
cReqChatVRange cReqChatVRange
Profile {displayName, fullName, image, contactLink, preferences} Profile {displayName, fullName, image}
profileId -- contact request profile id, to be used for member profile profileId -- contact request profile id, to be used for member profile
ldn -- contact request local display name, to be used for group local display name ldn -- contact request local display name, to be used for group local display name
groupPreferences = do groupPreferences = do
@ -1813,7 +1813,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.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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership} -- GroupInfo {membership}

View file

@ -441,9 +441,8 @@ data GroupLinkInfo = GroupLinkInfo
data AddressSettings = AddressSettings data AddressSettings = AddressSettings
{ businessAddress :: Bool, -- possibly, it can be wrapped together with acceptIncognito, or AutoAccept made sum type { businessAddress :: Bool, -- possibly, it can be wrapped together with acceptIncognito, or AutoAccept made sum type
welcomeMessage :: Maybe Text, -- included in short link information
autoAccept :: Maybe AutoAccept, -- accept automatically autoAccept :: Maybe AutoAccept, -- accept automatically
autoReply :: Maybe MsgContent -- sent on acceptance, can be supported with manual acceptance as well autoReply :: Maybe MsgContent -- included in short link information, sent on acceptance in case wasn't shown during connection
} }
deriving (Eq, Show) deriving (Eq, Show)
@ -458,11 +457,11 @@ $(J.deriveJSON defaultJSON ''AddressSettings)
$(J.deriveJSON defaultJSON ''UserContactLink) $(J.deriveJSON defaultJSON ''UserContactLink)
toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, Maybe Text, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI businessAddress, welcomeMessage, BI autoAccept', BI acceptIncognito, autoReply) = toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI businessAddress, BI autoAccept', BI acceptIncognito, autoReply) =
UserContactLink userContactLinkId (CCLink connReq shortLink) shortLinkDataSet $ UserContactLink userContactLinkId (CCLink connReq shortLink) shortLinkDataSet $
let autoAccept = if autoAccept' then Just AutoAccept {acceptIncognito} else Nothing let autoAccept = if autoAccept' then Just AutoAccept {acceptIncognito} else Nothing
in AddressSettings {businessAddress, welcomeMessage, autoAccept, autoReply} in AddressSettings {businessAddress, autoAccept, autoReply}
getUserAddress :: DB.Connection -> User -> ExceptT StoreError IO UserContactLink getUserAddress :: DB.Connection -> User -> ExceptT StoreError IO UserContactLink
getUserAddress db User {userId} = getUserAddress db User {userId} =
@ -475,7 +474,7 @@ getUserContactLinkById db userId userContactLinkId =
DB.query DB.query
db db
[sql| [sql|
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
FROM user_contact_links FROM user_contact_links
WHERE user_id = ? AND user_contact_link_id = ? WHERE user_id = ? AND user_contact_link_id = ?
|] |]
@ -511,7 +510,7 @@ getUserContactLinkViaShortLink db User {userId} shortLink =
userContactLinkQuery :: Query userContactLinkQuery :: Query
userContactLinkQuery = userContactLinkQuery =
[sql| [sql|
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links FROM user_contact_links
|] |]
@ -561,15 +560,15 @@ getContactWithoutConnViaShortAddress db vr user@User {userId} shortLink = do
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) ctId_ maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) ctId_
updateUserAddressSettings :: DB.Connection -> Int64 -> AddressSettings -> IO () updateUserAddressSettings :: DB.Connection -> Int64 -> AddressSettings -> IO ()
updateUserAddressSettings db userContactLinkId AddressSettings {businessAddress, welcomeMessage, autoAccept, autoReply} = updateUserAddressSettings db userContactLinkId AddressSettings {businessAddress, autoAccept, autoReply} =
DB.execute DB.execute
db db
[sql| [sql|
UPDATE user_contact_links UPDATE user_contact_links
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, address_welcome_message = ?, auto_reply_msg_content = ? SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?
WHERE user_contact_link_id = ? WHERE user_contact_link_id = ?
|] |]
(autoAcceptValues :. (businessAddress, welcomeMessage, autoReply, userContactLinkId)) (autoAcceptValues :. (businessAddress, autoReply, userContactLinkId))
where where
autoAcceptValues = case autoAccept of autoAcceptValues = case autoAccept of
Just AutoAccept {acceptIncognito} -> (BI True, BI acceptIncognito) Just AutoAccept {acceptIncognito} -> (BI True, BI acceptIncognito)

View file

@ -11,6 +11,7 @@ m20250526_short_links =
ALTER TABLE contacts ADD COLUMN conn_full_link_to_connect BLOB; ALTER TABLE contacts ADD COLUMN conn_full_link_to_connect BLOB;
ALTER TABLE contacts ADD COLUMN conn_short_link_to_connect BLOB; ALTER TABLE contacts ADD COLUMN conn_short_link_to_connect BLOB;
ALTER TABLE contacts ADD COLUMN welcome_shared_msg_id BLOB; ALTER TABLE contacts ADD COLUMN welcome_shared_msg_id BLOB;
ALTER TABLE contacts ADD COLUMN request_shared_msg_id BLOB;
ALTER TABLE contacts ADD COLUMN contact_request_id INTEGER REFERENCES contact_requests ON DELETE SET NULL; ALTER TABLE contacts ADD COLUMN contact_request_id INTEGER REFERENCES contact_requests ON DELETE SET NULL;
CREATE INDEX idx_contacts_contact_request_id ON contacts(contact_request_id); CREATE INDEX idx_contacts_contact_request_id ON contacts(contact_request_id);
@ -23,12 +24,12 @@ ALTER TABLE contact_requests ADD COLUMN request_shared_msg_id BLOB;
ALTER TABLE group_members ADD COLUMN member_xcontact_id BLOB; ALTER TABLE group_members ADD COLUMN member_xcontact_id BLOB;
ALTER TABLE user_contact_links ADD COLUMN short_link_data_set INTEGER NOT NULL DEFAULT 0; ALTER TABLE user_contact_links ADD COLUMN short_link_data_set INTEGER NOT NULL DEFAULT 0;
ALTER TABLE user_contact_links ADD COLUMN address_welcome_message TEXT;
ALTER TABLE groups ADD COLUMN conn_full_link_to_connect BLOB; ALTER TABLE groups ADD COLUMN conn_full_link_to_connect BLOB;
ALTER TABLE groups ADD COLUMN conn_short_link_to_connect BLOB; ALTER TABLE groups ADD COLUMN conn_short_link_to_connect BLOB;
ALTER TABLE groups ADD COLUMN conn_link_started_connection INTEGER NOT NULL DEFAULT 0; ALTER TABLE groups ADD COLUMN conn_link_started_connection INTEGER NOT NULL DEFAULT 0;
ALTER TABLE groups ADD COLUMN welcome_shared_msg_id BLOB; ALTER TABLE groups ADD COLUMN welcome_shared_msg_id BLOB;
ALTER TABLE groups ADD COLUMN request_shared_msg_id BLOB;
ALTER TABLE chat_items ADD COLUMN show_group_as_sender INTEGER NOT NULL DEFAULT 0; ALTER TABLE chat_items ADD COLUMN show_group_as_sender INTEGER NOT NULL DEFAULT 0;
|] |]
@ -39,6 +40,7 @@ down_m20250526_short_links =
ALTER TABLE contacts DROP COLUMN conn_full_link_to_connect; ALTER TABLE contacts DROP COLUMN conn_full_link_to_connect;
ALTER TABLE contacts DROP COLUMN conn_short_link_to_connect; ALTER TABLE contacts DROP COLUMN conn_short_link_to_connect;
ALTER TABLE contacts DROP COLUMN welcome_shared_msg_id; ALTER TABLE contacts DROP COLUMN welcome_shared_msg_id;
ALTER TABLE contacts DROP COLUMN request_shared_msg_id;
DROP INDEX idx_contacts_contact_request_id; DROP INDEX idx_contacts_contact_request_id;
ALTER TABLE contacts DROP COLUMN contact_request_id; ALTER TABLE contacts DROP COLUMN contact_request_id;
@ -51,12 +53,12 @@ ALTER TABLE contact_requests DROP COLUMN request_shared_msg_id;
ALTER TABLE group_members DROP COLUMN member_xcontact_id; ALTER TABLE group_members DROP COLUMN member_xcontact_id;
ALTER TABLE user_contact_links DROP COLUMN short_link_data_set; ALTER TABLE user_contact_links DROP COLUMN short_link_data_set;
ALTER TABLE user_contact_links DROP COLUMN address_welcome_message;
ALTER TABLE groups DROP COLUMN conn_full_link_to_connect; ALTER TABLE groups DROP COLUMN conn_full_link_to_connect;
ALTER TABLE groups DROP COLUMN conn_short_link_to_connect; ALTER TABLE groups DROP COLUMN conn_short_link_to_connect;
ALTER TABLE groups DROP COLUMN conn_link_started_connection; ALTER TABLE groups DROP COLUMN conn_link_started_connection;
ALTER TABLE groups DROP COLUMN welcome_shared_msg_id; ALTER TABLE groups DROP COLUMN welcome_shared_msg_id;
ALTER TABLE groups DROP COLUMN request_shared_msg_id;
ALTER TABLE chat_items DROP COLUMN show_group_as_sender; ALTER TABLE chat_items DROP COLUMN show_group_as_sender;
|] |]

View file

@ -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.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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership} -- GroupInfo {membership}
@ -197,7 +197,7 @@ Query:
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
@ -384,7 +384,7 @@ Plan:
Query: Query:
SELECT SELECT
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite, c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.contact_request_id, p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl
FROM contacts c FROM contacts c
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
@ -920,7 +920,7 @@ Query:
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
@ -945,7 +945,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image, 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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership} -- GroupInfo {membership}
@ -995,7 +995,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image, 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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, 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, 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,
@ -1504,7 +1504,7 @@ Query:
SELECT SELECT
-- Contact -- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite, ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.contact_request_id, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection -- Connection
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
@ -1692,25 +1692,6 @@ SEARCH cr USING INDEX idx_contact_requests_updated_at (user_id=?)
SEARCH p 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=?) 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.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: Query:
SELECT SELECT
created_at, updated_at, chat_ts, favorite, unread_chat created_at, updated_at, chat_ts, favorite, unread_chat
@ -3511,7 +3492,7 @@ Plan:
SEARCH usage_conditions USING INTEGER PRIMARY KEY (rowid=?) SEARCH usage_conditions USING INTEGER PRIMARY KEY (rowid=?)
Query: Query:
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
FROM user_contact_links FROM user_contact_links
WHERE user_id = ? AND user_contact_link_id = ? WHERE user_id = ? AND user_contact_link_id = ?
@ -4620,7 +4601,7 @@ SEARCH server_operators USING INTEGER PRIMARY KEY (rowid=?)
Query: Query:
UPDATE user_contact_links UPDATE user_contact_links
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, address_welcome_message = ?, auto_reply_msg_content = ? SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, auto_reply_msg_content = ?
WHERE user_contact_link_id = ? WHERE user_contact_link_id = ?
Plan: Plan:
@ -4686,7 +4667,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image, 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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership -- GroupMember - membership
@ -4712,7 +4693,7 @@ Query:
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image, 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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership -- GroupMember - membership
@ -4732,6 +4713,40 @@ SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
SEARCH mu USING INDEX idx_group_members_contact_id (contact_id=?) SEARCH mu USING INDEX idx_group_members_contact_id (contact_id=?)
SEARCH pu USING INTEGER PRIMARY KEY (rowid=?) SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
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.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.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: Query:
SELECT SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
@ -5171,21 +5186,21 @@ Plan:
SCAN usage_conditions SCAN usage_conditions
Query: Query:
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links FROM user_contact_links
WHERE user_id = ? AND conn_req_contact IN (?,?) WHERE user_id = ? AND conn_req_contact IN (?,?)
Plan: Plan:
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?) SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
Query: Query:
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links FROM user_contact_links
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
Plan: Plan:
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?) SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
Query: Query:
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, business_address, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links FROM user_contact_links
WHERE user_id = ? AND short_link_contact = ? WHERE user_id = ? AND short_link_contact = ?
Plan: Plan:
@ -6038,6 +6053,10 @@ Query: UPDATE contacts SET local_display_name = ?, updated_at = ? WHERE user_id
Plan: Plan:
SEARCH contacts USING INTEGER PRIMARY KEY (rowid=?) SEARCH contacts USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE contacts SET request_shared_msg_id = ? WHERE contact_id = ?
Plan:
SEARCH contacts USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE contacts SET send_rcpts = NULL Query: UPDATE contacts SET send_rcpts = NULL
Plan: Plan:
SCAN contacts SCAN contacts

View file

@ -82,6 +82,7 @@ CREATE TABLE contacts(
conn_full_link_to_connect BLOB, conn_full_link_to_connect BLOB,
conn_short_link_to_connect BLOB, conn_short_link_to_connect BLOB,
welcome_shared_msg_id BLOB, welcome_shared_msg_id BLOB,
request_shared_msg_id BLOB,
contact_request_id INTEGER REFERENCES contact_requests ON DELETE SET NULL, contact_request_id INTEGER REFERENCES contact_requests ON DELETE SET NULL,
FOREIGN KEY(user_id, local_display_name) FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name)
@ -143,7 +144,8 @@ CREATE TABLE groups(
conn_full_link_to_connect BLOB, conn_full_link_to_connect BLOB,
conn_short_link_to_connect BLOB, conn_short_link_to_connect BLOB,
conn_link_started_connection INTEGER NOT NULL DEFAULT 0, conn_link_started_connection INTEGER NOT NULL DEFAULT 0,
welcome_shared_msg_id BLOB, -- received welcome_shared_msg_id BLOB,
request_shared_msg_id BLOB, -- received
FOREIGN KEY(user_id, local_display_name) FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE ON DELETE CASCADE
@ -336,7 +338,6 @@ CREATE TABLE user_contact_links(
business_address INTEGER DEFAULT 0, business_address INTEGER DEFAULT 0,
short_link_contact BLOB, short_link_contact BLOB,
short_link_data_set INTEGER NOT NULL DEFAULT 0, short_link_data_set INTEGER NOT NULL DEFAULT 0,
address_welcome_message TEXT,
UNIQUE(user_id, local_display_name) UNIQUE(user_id, local_display_name)
); );
CREATE TABLE contact_requests( CREATE TABLE contact_requests(

View file

@ -431,7 +431,7 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
|] |]
(userId, profileId, userId, profileId, userId, profileId) (userId, profileId, userId, profileId, userId, profileId)
type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Maybe SharedMsgId) 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) 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)
@ -448,8 +448,9 @@ toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, via
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactGroupMemberId, contactGrpInvSent, chatTags, chatItemTTL, uiThemes, chatDeleted, customData} in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, preparedContact, contactRequestId, contactGroupMemberId, contactGrpInvSent, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
toPreparedContact :: PreparedContactRow -> Maybe PreparedContact toPreparedContact :: PreparedContactRow -> Maybe PreparedContact
toPreparedContact (connFullLink, connShortLink, welcomeSharedMsgId) = toPreparedContact (connFullLink, connShortLink, welcomeSharedMsgId, requestSharedMsgId) =
(\cl@(ACCL m _) -> PreparedContact cl (connMode m) welcomeSharedMsgId) <$> toACreatedConnLink_ connFullLink connShortLink (\cl@(ACCL m _) -> PreparedContact {connLinkToConnect = cl, uiConnLinkType = connMode m, welcomeSharedMsgId, requestSharedMsgId})
<$> toACreatedConnLink_ connFullLink connShortLink
toACreatedConnLink_ :: Maybe AConnectionRequestUri -> Maybe AConnShortLink -> Maybe ACreatedConnLink toACreatedConnLink_ :: Maybe AConnectionRequestUri -> Maybe AConnShortLink -> Maybe ACreatedConnLink
toACreatedConnLink_ Nothing _ = Nothing toACreatedConnLink_ Nothing _ = Nothing
@ -604,7 +605,7 @@ safeDeleteLDN db User {userId} localDisplayName = do
|] |]
(userId, localDisplayName, userId) (userId, localDisplayName, userId)
type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt, Maybe SharedMsgId) type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt, Maybe SharedMsgId, Maybe SharedMsgId)
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId) type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
@ -624,8 +625,8 @@ toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName,
toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup
toPreparedGroup = \case toPreparedGroup = \case
(Just fullLink, shortLink_, BI connLinkStartedConnection, welcomeSharedMsgId) -> (Just fullLink, shortLink_, BI connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId) ->
Just PreparedGroup {connLinkToConnect = CCLink fullLink shortLink_, connLinkStartedConnection, welcomeSharedMsgId} Just PreparedGroup {connLinkToConnect = CCLink fullLink shortLink_, connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId}
_ -> Nothing _ -> Nothing
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
@ -660,7 +661,7 @@ groupInfoQuery =
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image, 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.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.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.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.business_chat, g.business_member_id, g.customer_member_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, g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupMember - membership -- GroupMember - membership

View file

@ -207,7 +207,8 @@ contactRequestId' Contact {contactRequestId} = contactRequestId
data PreparedContact = PreparedContact data PreparedContact = PreparedContact
{ connLinkToConnect :: ACreatedConnLink, { connLinkToConnect :: ACreatedConnLink,
uiConnLinkType :: ConnectionMode, uiConnLinkType :: ConnectionMode,
welcomeSharedMsgId :: Maybe SharedMsgId welcomeSharedMsgId :: Maybe SharedMsgId,
requestSharedMsgId :: Maybe SharedMsgId
} }
deriving (Eq, Show) deriving (Eq, Show)
@ -404,8 +405,15 @@ data RequestEntity
type RepeatRequest = Bool type RepeatRequest = Bool
data RequestStage data RequestStage
= RSAcceptedRequest (Maybe UserContactRequest) RequestEntity -- Optional request is for legacy deleted requests = RSAcceptedRequest
| RSCurrentRequest UserContactRequest (Maybe RequestEntity) RepeatRequest -- Optional entity is for legacy requests without entity { acceptedRequest :: Maybe UserContactRequest, -- Request is optional to support deleted legacy requests
requestEntity :: RequestEntity
}
| RSCurrentRequest
{ previousRequest :: Maybe UserContactRequest,
currentRequest :: UserContactRequest,
requestEntity_ :: Maybe RequestEntity -- Entity is optional to support legacy requests without entity
}
type UserName = Text type UserName = Text
@ -488,7 +496,8 @@ instance ToField BusinessChatType where toField = toField . textEncode
data PreparedGroup = PreparedGroup data PreparedGroup = PreparedGroup
{ connLinkToConnect :: CreatedLinkContact, { connLinkToConnect :: CreatedLinkContact,
connLinkStartedConnection :: Bool, connLinkStartedConnection :: Bool,
welcomeSharedMsgId :: Maybe SharedMsgId -- it is stored only for business chats, and only if welcome message is specified welcomeSharedMsgId :: Maybe SharedMsgId, -- it is stored only for business chats, and only if welcome message is specified
requestSharedMsgId :: Maybe SharedMsgId
} }
deriving (Eq, Show) deriving (Eq, Show)
@ -715,18 +724,6 @@ instance ToField ImageData where toField (ImageData t) = toField t
deriving newtype instance FromField ImageData deriving newtype instance FromField ImageData
data ContactShortLinkData = ContactShortLinkData
{ profile :: Profile,
message :: Maybe Text,
business :: Bool
}
deriving (Show)
data GroupShortLinkData = GroupShortLinkData
{ groupProfile :: GroupProfile
}
deriving (Show)
data CReqClientData = CRDataGroup {groupLinkId :: GroupLinkId} data CReqClientData = CRDataGroup {groupLinkId :: GroupLinkId}
newtype GroupLinkId = GroupLinkId {unGroupLinkId :: ByteString} -- used to identify invitation via group link newtype GroupLinkId = GroupLinkId {unGroupLinkId :: ByteString} -- used to identify invitation via group link
@ -1998,10 +1995,6 @@ instance FromField MsgFilter where fromField = fromIntField_ msgFilterIntP
instance ToField MsgFilter where toField = toField . msgFilterInt instance ToField MsgFilter where toField = toField . msgFilterInt
$(JQ.deriveJSON defaultJSON ''ContactShortLinkData)
$(JQ.deriveJSON defaultJSON ''GroupShortLinkData)
$(JQ.deriveJSON defaultJSON ''CReqClientData) $(JQ.deriveJSON defaultJSON ''CReqClientData)
$(JQ.deriveJSON defaultJSON ''MemberIdRole) $(JQ.deriveJSON defaultJSON ''MemberIdRole)

View file

@ -1084,7 +1084,7 @@ simplexChatContact' = \case
-- TODO [short links] show all settings -- TODO [short links] show all settings
viewAddressSettings :: AddressSettings -> [StyledString] viewAddressSettings :: AddressSettings -> [StyledString]
viewAddressSettings AddressSettings {businessAddress, welcomeMessage = _, autoAccept, autoReply} = case autoAccept of viewAddressSettings AddressSettings {businessAddress, autoAccept, autoReply} = case autoAccept of
Just AutoAccept {acceptIncognito} -> Just AutoAccept {acceptIncognito} ->
("auto_accept on" <> aaInfo) ("auto_accept on" <> aaInfo)
: maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply : maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply

View file

@ -3662,8 +3662,9 @@ testShortLinkAddressChangeAutoReply =
alice <# "bob> hello" alice <# "bob> hello"
alice <## "bob (Bob): accepting contact request..." alice <## "bob (Bob): accepting contact request..."
alice <## "bob (Bob): you can send messages to contact" alice <## "bob (Bob): you can send messages to contact"
alice <# "@bob welcome!" -- auto reply -- welcome messages, not sent as events
bob <# "alice> welcome!" -- alice <# "@bob welcome!"
-- bob <# "alice> welcome!"
concurrently_ concurrently_
(bob <## "alice (Alice): contact is connected") (bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected") (alice <## "bob (Bob): contact is connected")