mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-06-04 08:11:57 +00:00
core: show group as sender, add contact card to contact request chats, unify api type for prepared and contact request chats (#6003)
* core: option to show group as sender of chat item (for the initial items, e.g. welcome message) * add chat item to contact request chats * return AChat for prepared chats and contact requests * update iOS api types, show prepared contact as blue, show preview info when content message is not available (previously was showing feature item) * ios: remove ContactType * ios: show group as sender, fix avatar not showing when member message sequence starts with merged items * ios: update compose UI for all connection scenarios * address settings * ios: address settings UI * fix tests * fix tests 2 * ios: fix minor issues
This commit is contained in:
@@ -64,7 +64,8 @@ initializeBotAddress' logAddress cc = do
|
||||
when logAddress $ do
|
||||
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)
|
||||
void $ sendChatCmd cc $ AddressAutoAccept $ Just AutoAccept {businessAddress = False, acceptIncognito = False, autoReply = Nothing}
|
||||
let settings = AddressSettings {businessAddress = False, welcomeMessage = Nothing, autoAccept = Just AutoAccept {acceptIncognito = False}, autoReply = Nothing}
|
||||
void $ sendChatCmd cc $ SetAddressSettings settings
|
||||
|
||||
sendMessage :: ChatController -> Contact -> Text -> IO ()
|
||||
sendMessage cc ct = sendComposedMessage cc ct Nothing . MCText
|
||||
|
||||
@@ -62,7 +62,7 @@ import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Remote.AppVersion
|
||||
import Simplex.Chat.Remote.Types
|
||||
import Simplex.Chat.Stats (PresentedServersSummary)
|
||||
import Simplex.Chat.Store (AutoAccept, ChatLockEntity, GroupLink, GroupLinkInfo, StoreError (..), UserContactLink, UserMsgReceiptSettings)
|
||||
import Simplex.Chat.Store (AddressSettings, ChatLockEntity, GroupLink, GroupLinkInfo, StoreError (..), UserContactLink, UserMsgReceiptSettings)
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
@@ -473,8 +473,8 @@ data ChatCommand
|
||||
| APIAddMyAddressShortLink UserId
|
||||
| APISetProfileAddress UserId Bool
|
||||
| SetProfileAddress Bool
|
||||
| APIAddressAutoAccept UserId (Maybe AutoAccept)
|
||||
| AddressAutoAccept (Maybe AutoAccept)
|
||||
| APISetAddressSettings UserId AddressSettings
|
||||
| SetAddressSettings AddressSettings
|
||||
| AcceptContact IncognitoEnabled ContactName
|
||||
| RejectContact ContactName
|
||||
| ForwardMessage {toChatName :: ChatName, fromContactName :: ContactName, forwardedMsg :: Text}
|
||||
@@ -685,8 +685,7 @@ data ChatResponse
|
||||
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||
| CRConnectionUserChanged {user :: User, fromConnection :: PendingContactConnection, toConnection :: PendingContactConnection, newUser :: User}
|
||||
| CRConnectionPlan {user :: User, connLink :: ACreatedConnLink, connectionPlan :: ConnectionPlan}
|
||||
| CRNewPreparedContact {user :: User, contact :: Contact}
|
||||
| CRNewPreparedGroup {user :: User, groupInfo :: GroupInfo}
|
||||
| CRNewPreparedChat {user :: User, chat :: AChat}
|
||||
| CRContactUserChanged {user :: User, fromContact :: Contact, newUser :: User, toContact :: Contact}
|
||||
| CRGroupUserChanged {user :: User, fromGroup :: GroupInfo, newUser :: User, toGroup :: GroupInfo}
|
||||
| CRSentConfirmation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||
@@ -785,7 +784,7 @@ data ChatEvent
|
||||
| CEvtGroupMemberUpdated {user :: User, groupInfo :: GroupInfo, fromMember :: GroupMember, toMember :: GroupMember}
|
||||
| CEvtContactsMerged {user :: User, intoContact :: Contact, mergedContact :: Contact, updatedContact :: Contact}
|
||||
| CEvtContactDeletedByContact {user :: User, contact :: Contact}
|
||||
| CEvtReceivedContactRequest {user :: User, contactRequest :: UserContactRequest, contact_ :: Maybe Contact}
|
||||
| CEvtReceivedContactRequest {user :: User, contactRequest :: UserContactRequest, chat_ :: Maybe AChat}
|
||||
| CEvtAcceptingContactRequest {user :: User, contact :: Contact} -- there is the same command response
|
||||
| CEvtAcceptingBusinessRequest {user :: User, groupInfo :: GroupInfo}
|
||||
| CEvtContactRequestAlreadyAccepted {user :: User, contact :: Contact}
|
||||
|
||||
@@ -99,7 +99,7 @@ import Simplex.Messaging.Compression (compressionLevel)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import Simplex.Messaging.Crypto.Ratchet (E2ERatchetParamsUri (..), PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQEncOff, pattern PQSupportOff, pattern PQSupportOn, pqRatchetE2EEncryptVersion)
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQEncOff, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (base64P)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), MsgFlags (..), NtfServer, ProtoServerWithAuth (..), ProtocolServer, ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode (..), UserProtocol, userProtocol)
|
||||
@@ -1735,29 +1735,31 @@ processChatCommand' vr = \case
|
||||
pure conn'
|
||||
APIConnectPlan userId cLink -> withUserId userId $ \user ->
|
||||
uncurry (CRConnectionPlan user) <$> connectPlan user cLink
|
||||
APIPrepareContact userId accLink@(ACCL cMode (CCLink _ shortLink)) contactSLinkData -> withUserId userId $ \user -> do
|
||||
APIPrepareContact userId accLink@(ACCL _ (CCLink cReq _)) contactSLinkData -> withUserId userId $ \user -> do
|
||||
let ContactShortLinkData {profile, message, business} = contactSLinkData
|
||||
-- TODO [short links] create business contact as group
|
||||
ct <- withStore $ \db -> createPreparedContact db user profile accLink
|
||||
let cMode' = connMode cMode
|
||||
createItem content = void $ createInternalItemForChat user (CDDirectRcv ct) content Nothing
|
||||
msgChatLink = \case
|
||||
sl@CSLContact {} -> MCLContact sl profile business
|
||||
sl@CSLInvitation {} -> MCLInvitation sl profile
|
||||
mapM_ (\sl -> createItem $ CIRcvMsgContent $ MCChat (safeDecodeUtf8 $ strEncode sl) $ msgChatLink sl) shortLink
|
||||
createItem $ CIRcvDirectE2EEInfo $ E2EInfo $ connLinkPQEncryption accLink
|
||||
let createItem content = createInternalItemForChat user (CDDirectRcv ct) False content Nothing
|
||||
cInfo = DirectChat ct
|
||||
void $ createItem $ CIRcvDirectE2EEInfo $ E2EInfo $ connRequestPQEncryption cReq
|
||||
void $ createFeatureEnabledItems_ user ct
|
||||
mapM_ (createItem . CIRcvMsgContent . MCText) message
|
||||
pure $ CRNewPreparedContact user ct
|
||||
APIPrepareGroup userId ccLink@(CCLink _ shortLink) groupSLinkData -> withUserId userId $ \user -> do
|
||||
aci <- mapM (createItem . CIRcvMsgContent . MCText) message
|
||||
let chat = case aci of
|
||||
Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
|
||||
_ -> Chat cInfo [] emptyChatStats
|
||||
pure $ CRNewPreparedChat user $ AChat SCTDirect chat
|
||||
APIPrepareGroup userId ccLink groupSLinkData -> withUserId userId $ \user -> do
|
||||
let GroupShortLinkData {groupProfile = gp@GroupProfile {description}} = groupSLinkData
|
||||
gInfo <- withStore $ \db -> createPreparedGroup db vr user gp ccLink
|
||||
-- TODO use received item without member
|
||||
let cd = CDGroupRcv gInfo Nothing $ membership gInfo
|
||||
createItem content = void $ createInternalItemForChat user cd content Nothing
|
||||
mapM_ (\sl -> createItem $ CIRcvMsgContent $ MCChat (safeDecodeUtf8 $ strEncode sl) $ MCLGroup sl gp) shortLink
|
||||
void $ createGroupFeatureItems_ user cd CIRcvGroupFeature gInfo
|
||||
mapM_ (createItem . CIRcvMsgContent . MCText) description
|
||||
pure $ CRNewPreparedGroup user gInfo
|
||||
(gInfo, hostMember) <- withStore $ \db -> createPreparedGroup db vr user gp ccLink
|
||||
let cd = CDGroupRcv gInfo Nothing hostMember
|
||||
createItem content = createInternalItemForChat user cd True content Nothing
|
||||
cInfo = GroupChat gInfo Nothing
|
||||
void $ createGroupFeatureItems_ user cd True CIRcvGroupFeature gInfo
|
||||
aci <- mapM (createItem . CIRcvMsgContent . MCText) description
|
||||
let chat = case aci of
|
||||
Just (AChatItem SCTGroup dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
|
||||
_ -> Chat cInfo [] emptyChatStats
|
||||
pure $ CRNewPreparedChat user $ AChat SCTGroup chat
|
||||
APIChangePreparedContactUser contactId newUserId -> withUser $ \user -> do
|
||||
ct@Contact {preparedContact} <- withFastStore $ \db -> getContact db vr user contactId
|
||||
when (isNothing preparedContact) $ throwCmdError "contact doesn't have link to connect"
|
||||
@@ -1866,9 +1868,8 @@ processChatCommand' vr = \case
|
||||
CRUserContactLink user <$> withFastStore (`getUserAddress` user)
|
||||
ShowMyAddress -> withUser' $ \User {userId} ->
|
||||
processChatCommand $ APIShowMyAddress userId
|
||||
APIAddMyAddressShortLink userId -> withUserId' userId $ \user -> do
|
||||
ucl <- withFastStore $ \db -> getUserAddress db user
|
||||
setMyAddressData user ucl
|
||||
APIAddMyAddressShortLink userId -> withUserId' userId $ \user ->
|
||||
CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData user)
|
||||
APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
|
||||
updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
|
||||
@@ -1879,27 +1880,20 @@ processChatCommand' vr = \case
|
||||
updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl
|
||||
SetProfileAddress onOff -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APISetProfileAddress userId onOff
|
||||
APIAddressAutoAccept userId autoAccept_ -> withUserId userId $ \user -> do
|
||||
ucl@UserContactLink {userContactLinkId, shortLinkDataSet, autoAccept} <- withFastStore (`getUserAddress` user)
|
||||
forM_ autoAccept_ $ \AutoAccept {businessAddress, acceptIncognito} -> do
|
||||
APISetAddressSettings userId settings@AddressSettings {businessAddress, autoAccept} -> withUserId userId $ \user -> do
|
||||
ucl@UserContactLink {userContactLinkId, shortLinkDataSet, addressSettings} <- withFastStore (`getUserAddress` user)
|
||||
forM_ autoAccept $ \AutoAccept {acceptIncognito} -> do
|
||||
when (shortLinkDataSet && acceptIncognito) $ throwCmdError "incognito not allowed for address with short link data"
|
||||
when (businessAddress && acceptIncognito) $ throwCmdError "requests to business address cannot be accepted incognito"
|
||||
let ucl' = ucl {autoAccept = autoAccept_}
|
||||
ucl'' <-
|
||||
if shortLinkDataSet && replyMsgChanged autoAccept autoAccept_
|
||||
then setMyAddressData user ucl' >>= \case
|
||||
CRUserContactLink _ ucl'' -> pure ucl''
|
||||
cr -> throwCmdError $ "unexpected response from setMyAddressData: " <> show cr
|
||||
else pure ucl'
|
||||
withFastStore' $ \db -> updateUserAddressAutoAccept db userContactLinkId autoAccept_
|
||||
pure $ CRUserContactLinkUpdated user ucl''
|
||||
where
|
||||
replyMsgChanged prevAutoAccept newAutoAccept =
|
||||
let prevReplyMsg = prevAutoAccept >>= autoReply
|
||||
newReplyMsg = newAutoAccept >>= autoReply
|
||||
in newReplyMsg /= prevReplyMsg
|
||||
AddressAutoAccept autoAccept_ -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddressAutoAccept userId autoAccept_
|
||||
if addressSettings == settings
|
||||
then pure $ CRUserContactLinkUpdated user ucl
|
||||
else do
|
||||
let ucl' = ucl {addressSettings = settings}
|
||||
ucl'' <- if shortLinkDataSet then setMyAddressData user ucl' else pure ucl'
|
||||
withFastStore' $ \db -> updateUserAddressSettings db userContactLinkId settings
|
||||
pure $ CRUserContactLinkUpdated user ucl''
|
||||
SetAddressSettings settings -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APISetAddressSettings userId settings
|
||||
AcceptContact incognito cName -> withUser $ \User {userId} -> do
|
||||
connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand $ APIAcceptContact incognito connReqId
|
||||
@@ -2957,7 +2951,8 @@ processChatCommand' vr = \case
|
||||
joinContact :: User -> Int64 -> ConnId -> ConnReqContact -> Maybe Profile -> XContactId -> Maybe MsgContent -> Bool -> PQSupport -> VersionChat -> CM ()
|
||||
joinContact user pccConnId connId cReq incognitoProfile xContactId mc_ inGroup pqSup chatV = do
|
||||
let profileToSend = userProfileToSend user incognitoProfile Nothing inGroup
|
||||
dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend (Just xContactId) mc_)
|
||||
-- TODO [short links] send welcome and sent sharedMsg Ids
|
||||
dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend (Just xContactId) Nothing ((SharedMsgId "\1\2\3\4",) <$> mc_))
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
joinPreparedAgentConnection user pccConnId connId cReq dm pqSup subMode
|
||||
joinPreparedAgentConnection :: User -> Int64 -> ConnId -> ConnectionRequestUri m -> ByteString -> PQSupport -> SubscriptionMode -> CM ()
|
||||
@@ -3036,17 +3031,17 @@ processChatCommand' vr = \case
|
||||
ctMsgReq ChangedProfileContact {conn} =
|
||||
fmap $ \SndMessage {msgId, msgBody} ->
|
||||
(conn, MsgFlags {notification = hasNotification XInfo_}, (vrValue msgBody, [msgId]))
|
||||
setMyAddressData :: User -> UserContactLink -> CM ChatResponse
|
||||
setMyAddressData user ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _sLnk_, autoAccept} = do
|
||||
setMyAddressData :: User -> UserContactLink -> CM UserContactLink
|
||||
setMyAddressData user ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _sLnk_, addressSettings} = do
|
||||
conn <- withFastStore $ \db -> getUserAddressConnection db vr user
|
||||
let shortLinkProfile = userProfileToSend user Nothing Nothing False
|
||||
shortLinkMsg = autoAccept >>= autoReply >>= (Just . msgContentText)
|
||||
userData <- contactShortLinkData shortLinkProfile shortLinkMsg
|
||||
-- TODO [short links] do not save address to server if data did not change, spinners, error handling
|
||||
userData <- contactShortLinkData shortLinkProfile $ Just addressSettings
|
||||
sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a (aConnId conn) SCMContact userData Nothing)
|
||||
withFastStore' $ \db -> setUserContactLinkShortLink db userContactLinkId sLnk
|
||||
let autoAccept' = autoAccept >>= \aa -> Just aa {acceptIncognito = False}
|
||||
ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk), shortLinkDataSet = True, autoAccept = autoAccept'}
|
||||
pure $ CRUserContactLink user ucl'
|
||||
let autoAccept' = (\aa -> aa {acceptIncognito = False}) <$> autoAccept addressSettings
|
||||
ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk), shortLinkDataSet = True, addressSettings = addressSettings {autoAccept = autoAccept'}}
|
||||
pure ucl'
|
||||
updateContactPrefs :: User -> Contact -> Preferences -> CM ChatResponse
|
||||
updateContactPrefs _ ct@Contact {activeConn = Nothing} _ = throwChatError $ CEContactNotActive ct
|
||||
updateContactPrefs user@User {userId} ct@Contact {activeConn = Just Connection {customUserProfileId}, userPreferences = contactUserPrefs} contactUserPrefs'
|
||||
@@ -3449,13 +3444,14 @@ processChatCommand' vr = \case
|
||||
CSLInvitation _ srv lnkId linkKey -> CSLInvitation SLSServer srv lnkId linkKey
|
||||
CSLContact _ ct srv linkKey -> CSLContact SLSServer ct srv linkKey
|
||||
restoreShortLink' l = (`restoreShortLink` l) <$> asks (shortLinkPresetServers . config)
|
||||
contactShortLinkData :: Profile -> Maybe Text -> CM UserLinkData
|
||||
contactShortLinkData p msg = do
|
||||
contactShortLinkData :: Profile -> Maybe AddressSettings -> CM UserLinkData
|
||||
contactShortLinkData p settings = do
|
||||
large <- chatReadVar useLargeLinkData
|
||||
-- TODO [short links] business
|
||||
let contactData
|
||||
| large = ContactShortLinkData p msg False
|
||||
| otherwise = ContactShortLinkData p {fullName = "", image = Nothing, contactLink = Nothing} Nothing False
|
||||
let msg = welcomeMessage =<< settings
|
||||
business = maybe False businessAddress settings
|
||||
contactData
|
||||
| large = ContactShortLinkData p msg business
|
||||
| otherwise = ContactShortLinkData p {fullName = "", image = Nothing, contactLink = Nothing} Nothing business
|
||||
pure $ encodeShortLinkData large contactData
|
||||
groupShortLinkData :: GroupProfile -> CM UserLinkData
|
||||
groupShortLinkData gp = do
|
||||
@@ -4508,8 +4504,8 @@ chatCommandP =
|
||||
"/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal),
|
||||
"/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP),
|
||||
("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP),
|
||||
"/_auto_accept " *> (APIAddressAutoAccept <$> A.decimal <* A.space <*> autoAcceptP),
|
||||
"/auto_accept " *> (AddressAutoAccept <$> autoAcceptP),
|
||||
"/_address_settings " *> (APISetAddressSettings <$> A.decimal <* A.space <*> jsonP),
|
||||
"/auto_accept " *> (SetAddressSettings <$> autoAcceptP),
|
||||
("/accept" <|> "/ac") *> (AcceptContact <$> incognitoP <* A.space <* char_ '@' <*> displayNameP),
|
||||
("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayNameP),
|
||||
("/markdown" <|> "/m") $> ChatHelp HSMarkdown,
|
||||
@@ -4756,10 +4752,11 @@ chatCommandP =
|
||||
nonEmptyKey k@(DBEncryptionKey s) = if BA.null s then Left "empty key" else Right k
|
||||
dbEncryptionConfig currentKey newKey = DBEncryptionConfig {currentKey, newKey, keepKey = Just False}
|
||||
#endif
|
||||
autoAcceptP = ifM onOffP (Just <$> (businessAA <|> addressAA)) (pure Nothing)
|
||||
-- TODO [short links] parser for address settings
|
||||
autoAcceptP = ifM onOffP (businessAA <|> addressAA) (pure $ AddressSettings False Nothing Nothing Nothing)
|
||||
where
|
||||
addressAA = AutoAccept False <$> (" incognito=" *> onOffP <|> pure False) <*> autoReply
|
||||
businessAA = AutoAccept True <$> (" business" *> pure False) <*> autoReply
|
||||
addressAA = AddressSettings False Nothing <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> autoReply
|
||||
businessAA = " business" *> (AddressSettings True Nothing (Just $ AutoAccept False) <$> autoReply)
|
||||
autoReply = optional (A.space *> msgContentP)
|
||||
rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P))
|
||||
text1P = safeDecodeUtf8 <$> A.takeTill (== ' ')
|
||||
|
||||
@@ -2100,7 +2100,7 @@ saveSndChatItems user cd itemsData itemTimed live = do
|
||||
createItem db createdAt NewSndChatItemData {msg = msg@SndMessage {sharedMsgId}, content, itemTexts, itemMentions, ciFile, quotedItem, itemForwarded} = do
|
||||
ciId <- createNewSndChatItem db user cd msg content quotedItem itemForwarded itemTimed live createdAt
|
||||
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
|
||||
let ci = mkChatItem_ cd ciId content itemTexts ciFile quotedItem (Just sharedMsgId) itemForwarded itemTimed live False createdAt Nothing createdAt
|
||||
let ci = mkChatItem_ cd False ciId content itemTexts ciFile quotedItem (Just sharedMsgId) itemForwarded itemTimed live False createdAt Nothing createdAt
|
||||
Right <$> case cd of
|
||||
CDGroupSnd g _scope | not (null itemMentions) -> createGroupCIMentions db g ci itemMentions
|
||||
_ -> pure ci
|
||||
@@ -2134,7 +2134,7 @@ saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} shared
|
||||
else pure $ toChatInfo cd
|
||||
(ciId, quotedItem, itemForwarded) <- createNewRcvChatItem db user cd msg sharedMsgId_ content itemTimed live userMention brokerTs createdAt
|
||||
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
|
||||
let ci = mkChatItem_ cd ciId content (t, ft_) ciFile quotedItem sharedMsgId_ itemForwarded itemTimed live userMention brokerTs forwardedByMember createdAt
|
||||
let ci = mkChatItem_ cd False ciId content (t, ft_) ciFile quotedItem sharedMsgId_ itemForwarded itemTimed live userMention brokerTs forwardedByMember createdAt
|
||||
ci' <- case cd of
|
||||
CDGroupRcv g _scope _m | not (null mentions') -> createGroupCIMentions db g ci mentions'
|
||||
_ -> pure ci
|
||||
@@ -2148,15 +2148,15 @@ saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} shared
|
||||
_ -> Nothing
|
||||
|
||||
-- TODO [mentions] optimize by avoiding unnecessary parsing
|
||||
mkChatItem :: (ChatTypeI c, MsgDirectionI d) => ChatDirection c d -> ChatItemId -> CIContent d -> Maybe (CIFile d) -> Maybe (CIQuote c) -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> ChatItem c d
|
||||
mkChatItem cd ciId content file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs =
|
||||
mkChatItem :: (ChatTypeI c, MsgDirectionI d) => ChatDirection c d -> ShowGroupAsSender -> ChatItemId -> CIContent d -> Maybe (CIFile d) -> Maybe (CIQuote c) -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> ChatItem c d
|
||||
mkChatItem cd showGroupAsSender ciId content file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs =
|
||||
let ts = ciContentTexts content
|
||||
in mkChatItem_ cd ciId content ts file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs
|
||||
in mkChatItem_ cd showGroupAsSender ciId content ts file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs
|
||||
|
||||
mkChatItem_ :: (ChatTypeI c, MsgDirectionI d) => ChatDirection c d -> ChatItemId -> CIContent d -> (Text, Maybe MarkdownList) -> Maybe (CIFile d) -> Maybe (CIQuote c) -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> ChatItem c d
|
||||
mkChatItem_ cd ciId content (itemText, formattedText) file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs =
|
||||
mkChatItem_ :: (ChatTypeI c, MsgDirectionI d) => ChatDirection c d -> ShowGroupAsSender -> ChatItemId -> CIContent d -> (Text, Maybe MarkdownList) -> Maybe (CIFile d) -> Maybe (CIQuote c) -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> ChatItem c d
|
||||
mkChatItem_ cd showGroupAsSender ciId content (itemText, formattedText) file quotedItem sharedMsgId itemForwarded itemTimed live userMention itemTs forwardedByMember currentTs =
|
||||
let itemStatus = ciCreateStatus content
|
||||
meta = mkCIMeta ciId content itemText itemStatus Nothing sharedMsgId itemForwarded Nothing False itemTimed (justTrue live) userMention currentTs itemTs forwardedByMember currentTs currentTs
|
||||
meta = mkCIMeta ciId content itemText itemStatus Nothing sharedMsgId itemForwarded Nothing False itemTimed (justTrue live) userMention currentTs itemTs forwardedByMember showGroupAsSender currentTs currentTs
|
||||
in ChatItem {chatDir = toCIDirection cd, meta, content, mentions = M.empty, formattedText, quotedItem, reactions = [], file}
|
||||
|
||||
createAgentConnectionAsync :: ConnectionModeI c => User -> CommandFunction -> Bool -> SConnectionMode c -> SubscriptionMode -> CM (CommandId, ConnId)
|
||||
@@ -2262,8 +2262,8 @@ userProfileToSend user@User {profile = p} incognitoProfile ct inGroup = do
|
||||
let userPrefs = maybe (preferences' user) (const Nothing) incognitoProfile
|
||||
in (p' :: Profile) {preferences = Just . toChatPrefs $ mergePreferences (userPreferences <$> ct) userPrefs}
|
||||
|
||||
connLinkPQEncryption :: ACreatedConnLink -> Maybe PQEncryption
|
||||
connLinkPQEncryption (ACCL _ (CCLink cReq _)) = case cReq of
|
||||
connRequestPQEncryption :: ConnectionRequestUri c -> Maybe PQEncryption
|
||||
connRequestPQEncryption = \case
|
||||
CRContactUri _ -> Nothing
|
||||
CRInvitationUri _ (CR.E2ERatchetParamsUri vr' _ _ pq) ->
|
||||
Just $ PQEncryption $ maxVersion vr' >= CR.pqRatchetE2EEncryptVersion && isJust pq
|
||||
@@ -2289,7 +2289,7 @@ createFeatureEnabledItems_ :: User -> Contact -> CM [AChatItem]
|
||||
createFeatureEnabledItems_ user ct@Contact {mergedPreferences} =
|
||||
forM allChatFeatures $ \(ACF f) -> do
|
||||
let state = featureState $ getContactUserPreference f mergedPreferences
|
||||
createInternalItemForChat user (CDDirectRcv ct) (uncurry (CIRcvChatFeature $ chatFeature f) state) Nothing
|
||||
createInternalItemForChat user (CDDirectRcv ct) False (uncurry (CIRcvChatFeature $ chatFeature f) state) Nothing
|
||||
|
||||
createFeatureItems ::
|
||||
MsgDirectionI d =>
|
||||
@@ -2319,10 +2319,10 @@ createContactsFeatureItems user cts chatDir ciFeature ciOffer getPref = do
|
||||
unless (null errs) $ toView' $ CEvtChatErrors errs
|
||||
toView' $ CEvtNewChatItems user acis
|
||||
where
|
||||
contactChangedFeatures :: (Contact, Contact) -> (ChatDirection 'CTDirect d, [CIContent d])
|
||||
contactChangedFeatures :: (Contact, Contact) -> (ChatDirection 'CTDirect d, ShowGroupAsSender, [CIContent d])
|
||||
contactChangedFeatures (Contact {mergedPreferences = cups}, ct'@Contact {mergedPreferences = cups'}) = do
|
||||
let contents = mapMaybe (\(ACF f) -> featureCIContent_ f) allChatFeatures
|
||||
(chatDir ct', contents)
|
||||
(chatDir ct', False, contents)
|
||||
where
|
||||
featureCIContent_ :: forall f. FeatureI f => SChatFeature f -> Maybe (CIContent d)
|
||||
featureCIContent_ f
|
||||
@@ -2353,23 +2353,23 @@ sameGroupProfileInfo :: GroupProfile -> GroupProfile -> Bool
|
||||
sameGroupProfileInfo p p' = p {groupPreferences = Nothing} == p' {groupPreferences = Nothing}
|
||||
|
||||
createGroupFeatureItems :: MsgDirectionI d => User -> ChatDirection 'CTGroup d -> (GroupFeature -> GroupPreference -> Maybe Int -> Maybe GroupMemberRole -> CIContent d) -> GroupInfo -> CM ()
|
||||
createGroupFeatureItems user cd ciContent g = createGroupFeatureItems_ user cd ciContent g >>= toView . CEvtNewChatItems user
|
||||
createGroupFeatureItems user cd ciContent g = createGroupFeatureItems_ user cd False ciContent g >>= toView . CEvtNewChatItems user
|
||||
|
||||
createGroupFeatureItems_ :: MsgDirectionI d => User -> ChatDirection 'CTGroup d -> (GroupFeature -> GroupPreference -> Maybe Int -> Maybe GroupMemberRole -> CIContent d) -> GroupInfo -> CM [AChatItem]
|
||||
createGroupFeatureItems_ user cd ciContent GroupInfo {fullGroupPreferences} =
|
||||
createGroupFeatureItems_ :: MsgDirectionI d => User -> ChatDirection 'CTGroup d -> Bool -> (GroupFeature -> GroupPreference -> Maybe Int -> Maybe GroupMemberRole -> CIContent d) -> GroupInfo -> CM [AChatItem]
|
||||
createGroupFeatureItems_ user cd showGroupAsSender ciContent GroupInfo {fullGroupPreferences} =
|
||||
forM allGroupFeatures $ \(AGF f) -> do
|
||||
let p = getGroupPreference f fullGroupPreferences
|
||||
(_, param, role) = groupFeatureState p
|
||||
createInternalItemForChat user cd (ciContent (toGroupFeature f) (toGroupPreference p) param role) Nothing
|
||||
createInternalItemForChat user cd showGroupAsSender (ciContent (toGroupFeature f) (toGroupPreference p) param role) Nothing
|
||||
|
||||
createInternalChatItem :: (ChatTypeI c, MsgDirectionI d) => User -> ChatDirection c d -> CIContent d -> Maybe UTCTime -> CM ()
|
||||
createInternalChatItem user cd content itemTs_ = do
|
||||
ci <- createInternalItemForChat user cd content itemTs_
|
||||
ci <- createInternalItemForChat user cd False content itemTs_
|
||||
toView $ CEvtNewChatItems user [ci]
|
||||
|
||||
createInternalItemForChat :: (ChatTypeI c, MsgDirectionI d) => User -> ChatDirection c d -> CIContent d -> Maybe UTCTime -> CM AChatItem
|
||||
createInternalItemForChat user cd content itemTs_ =
|
||||
lift (createInternalItemsForChats user itemTs_ [(cd, [content])]) >>= \case
|
||||
createInternalItemForChat :: (ChatTypeI c, MsgDirectionI d) => User -> ChatDirection c d -> Bool -> CIContent d -> Maybe UTCTime -> CM AChatItem
|
||||
createInternalItemForChat user cd showGroupAsSender content itemTs_ =
|
||||
lift (createInternalItemsForChats user itemTs_ [(cd, showGroupAsSender, [content])]) >>= \case
|
||||
[Right ci] -> pure ci
|
||||
[Left e] -> throwError e
|
||||
rs -> throwChatError $ CEInternalError $ "createInternalChatItem: expected 1 result, got " <> show (length rs)
|
||||
@@ -2379,17 +2379,17 @@ createInternalItemsForChats ::
|
||||
(ChatTypeI c, MsgDirectionI d) =>
|
||||
User ->
|
||||
Maybe UTCTime ->
|
||||
[(ChatDirection c d, [CIContent d])] ->
|
||||
[(ChatDirection c d, ShowGroupAsSender, [CIContent d])] ->
|
||||
CM' [Either ChatError AChatItem]
|
||||
createInternalItemsForChats user itemTs_ dirsCIContents = do
|
||||
createdAt <- liftIO getCurrentTime
|
||||
let itemTs = fromMaybe createdAt itemTs_
|
||||
vr <- chatVersionRange'
|
||||
void . withStoreBatch' $ \db -> map (uncurry $ updateChat db vr createdAt) dirsCIContents
|
||||
withStoreBatch' $ \db -> concatMap (uncurry $ createACIs db itemTs createdAt) dirsCIContents
|
||||
void . withStoreBatch' $ \db -> map (updateChat db vr createdAt) dirsCIContents
|
||||
withStoreBatch' $ \db -> concatMap (createACIs db itemTs createdAt) dirsCIContents
|
||||
where
|
||||
updateChat :: DB.Connection -> VersionRangeChat -> UTCTime -> ChatDirection c d -> [CIContent d] -> IO ()
|
||||
updateChat db vr createdAt cd contents
|
||||
updateChat :: DB.Connection -> VersionRangeChat -> UTCTime -> (ChatDirection c d, ShowGroupAsSender, [CIContent d]) -> IO ()
|
||||
updateChat db vr createdAt (cd, _, contents)
|
||||
| any ciRequiresAttention contents || contactChatDeleted cd = void $ updateChatTsStats db vr user cd createdAt memberChatStats
|
||||
| otherwise = pure ()
|
||||
where
|
||||
@@ -2399,11 +2399,13 @@ createInternalItemsForChats user itemTs_ dirsCIContents = do
|
||||
let unread = length $ filter ciRequiresAttention contents
|
||||
in Just (unread, memberAttentionChange unread itemTs_ m scope, 0)
|
||||
_ -> Nothing
|
||||
createACIs :: DB.Connection -> UTCTime -> UTCTime -> ChatDirection c d -> [CIContent d] -> [IO AChatItem]
|
||||
createACIs db itemTs createdAt cd = map $ \content -> do
|
||||
ciId <- createNewChatItemNoMsg db user cd content itemTs createdAt
|
||||
let ci = mkChatItem cd ciId content Nothing Nothing Nothing Nothing Nothing False False itemTs Nothing createdAt
|
||||
pure $ AChatItem (chatTypeI @c) (msgDirection @d) (toChatInfo cd) ci
|
||||
createACIs :: DB.Connection -> UTCTime -> UTCTime -> (ChatDirection c d, ShowGroupAsSender, [CIContent d]) -> [IO AChatItem]
|
||||
createACIs db itemTs createdAt (cd, showGroupAsSender, contents) = map createACI contents
|
||||
where
|
||||
createACI content = do
|
||||
ciId <- createNewChatItemNoMsg db user cd showGroupAsSender content itemTs createdAt
|
||||
let ci = mkChatItem cd showGroupAsSender ciId content Nothing Nothing Nothing Nothing Nothing False False itemTs Nothing createdAt
|
||||
pure $ AChatItem (chatTypeI @c) (msgDirection @d) (toChatInfo cd) ci
|
||||
|
||||
memberAttentionChange :: Int -> (Maybe UTCTime) -> GroupMember -> GroupChatScopeInfo -> MemberAttention
|
||||
memberAttentionChange unread brokerTs_ rcvMem = \case
|
||||
@@ -2432,9 +2434,9 @@ createLocalChatItems user cd itemsData createdAt = do
|
||||
where
|
||||
createItem :: DB.Connection -> (CIContent 'MDSnd, Maybe (CIFile 'MDSnd), Maybe CIForwardedFrom, (Text, Maybe MarkdownList)) -> IO (ChatItem 'CTLocal 'MDSnd)
|
||||
createItem db (content, ciFile, itemForwarded, ts) = do
|
||||
ciId <- createNewChatItem_ db user cd Nothing Nothing content (Nothing, Nothing, Nothing, Nothing, Nothing) itemForwarded Nothing False False createdAt Nothing createdAt
|
||||
ciId <- createNewChatItem_ db user cd False Nothing Nothing content (Nothing, Nothing, Nothing, Nothing, Nothing) itemForwarded Nothing False False createdAt Nothing createdAt
|
||||
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
|
||||
pure $ mkChatItem_ cd ciId content ts ciFile Nothing Nothing itemForwarded Nothing False False createdAt Nothing createdAt
|
||||
pure $ mkChatItem_ cd False ciId content ts ciFile Nothing Nothing itemForwarded Nothing False False createdAt Nothing createdAt
|
||||
|
||||
withUser' :: (User -> CM ChatResponse) -> CM ChatResponse
|
||||
withUser' action =
|
||||
|
||||
@@ -557,7 +557,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- TODO update member profile
|
||||
pure ()
|
||||
XInfo profile -> do
|
||||
let prepared = isJust $ preparedContact ct
|
||||
let prepared = isJust (preparedContact ct) || isJust (contactRequestId' ct)
|
||||
void $ processContactProfileUpdate ct profile prepared
|
||||
XOk -> pure ()
|
||||
_ -> messageError "INFO for existing contact must have x.grp.mem.info, x.info or x.ok"
|
||||
@@ -572,12 +572,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
lift $ setContactNetworkStatus ct' NSConnected
|
||||
toView $ CEvtContactConnected user ct' (fmap fromLocalProfile incognitoProfile)
|
||||
let createE2EItem = createInternalChatItem user (CDDirectRcv ct') (CIRcvDirectE2EEInfo $ E2EInfo $ Just pqEnc) Nothing
|
||||
when (directOrUsed ct') $ case preparedContact ct' of
|
||||
Nothing -> do
|
||||
when (directOrUsed ct') $ case (preparedContact ct', contactRequestId' ct') of
|
||||
(Nothing, Nothing) -> do
|
||||
createE2EItem
|
||||
createFeatureEnabledItems user ct'
|
||||
Just PreparedContact {connLinkToConnect = cl} ->
|
||||
unless (Just pqEnc == connLinkPQEncryption cl) createE2EItem
|
||||
(Just PreparedContact {connLinkToConnect = ACCL _ (CCLink cReq _)}, _) ->
|
||||
unless (Just pqEnc == connRequestPQEncryption cReq) createE2EItem
|
||||
(_, Just connReqId) -> do
|
||||
UserContactRequest {pqSupport} <- withStore $ \db -> getContactRequest db user connReqId
|
||||
unless (CR.pqSupportToEnc pqSupport == pqEnc) createE2EItem
|
||||
when (contactConnInitiated conn') $ do
|
||||
let Connection {groupLinkId} = conn'
|
||||
doProbeContacts = isJust groupLinkId
|
||||
@@ -673,13 +676,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- TODO add debugging output
|
||||
_ -> pure ()
|
||||
where
|
||||
sendAutoReply UserContactLink {shortLinkDataSet, autoAccept} ct = case autoAccept of
|
||||
Just AutoAccept {autoReply = Just mc}
|
||||
| not shortLinkDataSet || connChatVersion < shortLinkDataVersion -> 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 ()
|
||||
sendAutoReply UserContactLink {addressSettings = AddressSettings {autoReply}} ct =
|
||||
forM_ autoReply $ \mc -> 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]
|
||||
|
||||
processGroupMessage :: AEvent e -> ConnectionEntity -> Connection -> GroupInfo -> GroupMember -> CM ()
|
||||
processGroupMessage agentMsg connEntity conn@Connection {connId, connChatVersion, customUserProfileId, connectionCode} gInfo@GroupInfo {groupId, groupProfile, membership, chatSettings} m = case agentMsg of
|
||||
@@ -1045,11 +1046,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
Just BusinessChatInfo {customerId, chatType = BCCustomer}
|
||||
| joiningMemberId == customerId -> useReply <$> withStore (`getUserAddress` user)
|
||||
where
|
||||
useReply UserContactLink {autoAccept, shortLinkDataSet} = case autoAccept of
|
||||
Just AutoAccept {businessAddress, autoReply}
|
||||
| businessAddress && (not shortLinkDataSet || connChatVersion < shortLinkDataVersion) ->
|
||||
autoReply
|
||||
_ -> Nothing
|
||||
useReply UserContactLink {addressSettings = AddressSettings {autoReply}} = autoReply
|
||||
_ -> pure Nothing
|
||||
send mc = do
|
||||
msg <- sendGroupMessage' user gInfo [m] (XMsgNew $ MCSimple (extMsgContent mc Nothing))
|
||||
@@ -1224,8 +1221,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
REQ invId pqSupport _ connInfo -> do
|
||||
ChatMessage {chatVRange, chatMsgEvent} <- parseChatMessage conn connInfo
|
||||
case chatMsgEvent of
|
||||
XContact p xContactId_ mc_ -> profileContactRequest invId chatVRange p xContactId_ mc_ pqSupport
|
||||
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing pqSupport
|
||||
XContact p xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ pqSupport
|
||||
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing Nothing pqSupport
|
||||
-- TODO show/log error, other events in contact request
|
||||
_ -> pure ()
|
||||
MERR _ err -> do
|
||||
@@ -1237,22 +1234,39 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- TODO add debugging output
|
||||
_ -> pure ()
|
||||
where
|
||||
profileContactRequest :: InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> Maybe MsgContent -> PQSupport -> CM ()
|
||||
profileContactRequest invId chatVRange p@Profile {displayName} xContactId_ mc_ reqPQSup = do
|
||||
profileContactRequest :: InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> Maybe SharedMsgId -> Maybe (SharedMsgId, MsgContent) -> PQSupport -> CM ()
|
||||
profileContactRequest invId chatVRange p@Profile {displayName} xContactId_ welcomeMsgId_ requestMsg_ reqPQSup = do
|
||||
uclGLinkInfo <- withStore $ \db -> getUserContactLinkById db userId uclId
|
||||
let (UserContactLink {connLinkContact = CCLink connReq _, shortLinkDataSet, autoAccept}, gLinkInfo_) = uclGLinkInfo
|
||||
let (UserContactLink {connLinkContact = CCLink connReq _, shortLinkDataSet, addressSettings}, gLinkInfo_) = uclGLinkInfo
|
||||
AddressSettings {businessAddress, autoAccept} = addressSettings
|
||||
isSimplexTeam = sameConnReqContact connReq adminContactReq
|
||||
v = maxVersion chatVRange
|
||||
case autoAccept of
|
||||
Nothing ->
|
||||
withStore (\db -> createOrUpdateContactRequest db vr user uclId invId chatVRange p xContactId_ reqPQSup) >>= \case
|
||||
CORContact ct -> toView $ CEvtContactRequestAlreadyAccepted user ct
|
||||
CORRequest cReq ct_ -> do
|
||||
forM_ ct_ $ \ct ->
|
||||
forM_ mc_ $ \mc ->
|
||||
createInternalChatItem user (CDDirectRcv ct) (CIRcvMsgContent mc) Nothing
|
||||
toView $ CEvtReceivedContactRequest user cReq ct_
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply}
|
||||
CORRequest cReq ct_ newRequest -> do
|
||||
chat_ <- forM ct_ $ \ct -> do
|
||||
-- TODO [short links] prevent duplicate items
|
||||
-- update welcome message if changed (send update event to UI) and add updated feature items.
|
||||
-- Do not created e2e item on repeat request
|
||||
if newRequest
|
||||
then do
|
||||
let createItem content = createInternalItemForChat user (CDDirectRcv ct) False content Nothing
|
||||
void $ createItem $ CIRcvDirectE2EEInfo $ E2EInfo $ Just $ CR.pqSupportToEnc $ reqPQSup
|
||||
void $ createFeatureEnabledItems_ user ct
|
||||
-- TODO [short links] save sharedMsgId
|
||||
aci <- forM requestMsg_ $ \(sharedMsgId, mc) -> do
|
||||
aci <- createItem $ CIRcvMsgContent mc
|
||||
unlessM (asks $ coreApi . config) $ toView $ CEvtNewChatItems user [aci]
|
||||
pure aci
|
||||
let cInfo = DirectChat ct
|
||||
pure $ AChat SCTDirect $ case aci of
|
||||
Just (AChatItem SCTDirect dir _ ci) -> Chat cInfo [CChatItem dir ci] emptyChatStats {unreadCount = 1, minUnreadItemId = chatItemId' ci}
|
||||
_ -> Chat cInfo [] emptyChatStats
|
||||
else pure $ AChat SCTDirect $ Chat (DirectChat ct) [] emptyChatStats
|
||||
toView $ CEvtReceivedContactRequest user cReq chat_
|
||||
Just AutoAccept {acceptIncognito}
|
||||
| businessAddress ->
|
||||
if isSimplexTeam && v < businessChatsVersion
|
||||
then
|
||||
@@ -1260,10 +1274,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
Just ct -> toView $ CEvtContactRequestAlreadyAccepted user ct
|
||||
Nothing -> do
|
||||
ct <- acceptContactRequestAsync user uclId invId chatVRange p xContactId_ reqPQSup Nothing
|
||||
forM_ autoReply $ \arMC ->
|
||||
when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
createInternalChatItem user (CDDirectSnd ct) (CISndMsgContent arMC) Nothing
|
||||
forM_ mc_ $ \mc ->
|
||||
-- TODO [short links] add welcome message if welcomeMsgId is present
|
||||
-- forM_ autoReply $ \arMC ->
|
||||
-- when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
-- createInternalChatItem user (CDDirectSnd ct) (CISndMsgContent arMC) Nothing
|
||||
-- TODO [short links] save sharedMsgId
|
||||
forM_ requestMsg_ $ \(sharedMsgId, mc) ->
|
||||
createInternalChatItem user (CDDirectRcv ct) (CIRcvMsgContent mc) Nothing
|
||||
toView $ CEvtAcceptingContactRequest user ct
|
||||
else
|
||||
@@ -1271,10 +1287,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
Just gInfo -> toView $ CEvtBusinessRequestAlreadyAccepted user gInfo
|
||||
Nothing -> do
|
||||
(gInfo, clientMember) <- acceptBusinessJoinRequestAsync user uclId invId chatVRange p xContactId_
|
||||
forM_ autoReply $ \arMC ->
|
||||
when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
createInternalChatItem user (CDGroupSnd gInfo Nothing) (CISndMsgContent arMC) Nothing
|
||||
forM_ mc_ $ \mc ->
|
||||
-- TODO [short links] add welcome message if welcomeMsgId is present
|
||||
-- forM_ autoReply $ \arMC ->
|
||||
-- when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
-- createInternalChatItem user (CDGroupSnd gInfo Nothing) (CISndMsgContent arMC) Nothing
|
||||
-- TODO [short links] save sharedMsgId
|
||||
forM_ requestMsg_ $ \(sharedMsgId, mc) ->
|
||||
createInternalChatItem user (CDGroupRcv gInfo Nothing clientMember) (CIRcvMsgContent mc) Nothing
|
||||
toView $ CEvtAcceptingBusinessRequest user gInfo
|
||||
| otherwise -> case gLinkInfo_ of
|
||||
@@ -1288,10 +1306,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
then Just . NewIncognito <$> liftIO generateRandomProfile
|
||||
else pure Nothing
|
||||
ct <- acceptContactRequestAsync user uclId invId chatVRange p xContactId_ reqPQSup incognitoProfile
|
||||
forM_ autoReply $ \arMC ->
|
||||
when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
createInternalChatItem user (CDDirectSnd ct) (CISndMsgContent arMC) Nothing
|
||||
forM_ mc_ $ \mc ->
|
||||
-- TODO [short links] add welcome message if welcomeMsgId is present
|
||||
-- forM_ autoReply $ \arMC ->
|
||||
-- when (shortLinkDataSet && v >= shortLinkDataVersion) $
|
||||
-- createInternalChatItem user (CDDirectSnd ct) (CISndMsgContent arMC) Nothing
|
||||
-- TODO [short links] save sharedMsgId
|
||||
forM_ requestMsg_ $ \(sharedMsgId, mc) ->
|
||||
createInternalChatItem user (CDDirectRcv ct) (CIRcvMsgContent mc) Nothing
|
||||
toView $ CEvtAcceptingContactRequest user ct
|
||||
Just gli@GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do
|
||||
|
||||
@@ -436,16 +436,19 @@ data CIMeta (c :: ChatType) (d :: MsgDirection) = CIMeta
|
||||
deletable :: Bool,
|
||||
editable :: Bool,
|
||||
forwardedByMember :: Maybe GroupMemberId,
|
||||
showGroupAsSender :: ShowGroupAsSender,
|
||||
createdAt :: UTCTime,
|
||||
updatedAt :: UTCTime
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
mkCIMeta :: forall c d. ChatTypeI c => ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe Bool -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> Bool -> UTCTime -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> UTCTime -> CIMeta c d
|
||||
mkCIMeta itemId itemContent itemText itemStatus sentViaProxy itemSharedMsgId itemForwarded itemDeleted itemEdited itemTimed itemLive userMention currentTs itemTs forwardedByMember createdAt updatedAt =
|
||||
type ShowGroupAsSender = Bool
|
||||
|
||||
mkCIMeta :: forall c d. ChatTypeI c => ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe Bool -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> Bool -> UTCTime -> ChatItemTs -> Maybe GroupMemberId -> Bool -> UTCTime -> UTCTime -> CIMeta c d
|
||||
mkCIMeta itemId itemContent itemText itemStatus sentViaProxy itemSharedMsgId itemForwarded itemDeleted itemEdited itemTimed itemLive userMention currentTs itemTs forwardedByMember showGroupAsSender createdAt updatedAt =
|
||||
let deletable = deletable' itemContent itemDeleted itemTs nominalDay currentTs
|
||||
editable = deletable && isNothing itemForwarded
|
||||
in CIMeta {itemId, itemTs, itemText, itemStatus, sentViaProxy, itemSharedMsgId, itemForwarded, itemDeleted, itemEdited, itemTimed, itemLive, userMention, deletable, editable, forwardedByMember, createdAt, updatedAt}
|
||||
in CIMeta {itemId, itemTs, itemText, itemStatus, sentViaProxy, itemSharedMsgId, itemForwarded, itemDeleted, itemEdited, itemTimed, itemLive, userMention, deletable, editable, forwardedByMember, showGroupAsSender, createdAt, updatedAt}
|
||||
|
||||
deletable' :: forall c d. ChatTypeI c => CIContent d -> Maybe (CIDeleted c) -> UTCTime -> NominalDiffTime -> UTCTime -> Bool
|
||||
deletable' itemContent itemDeleted itemTs allowedInterval currentTs =
|
||||
@@ -474,6 +477,7 @@ dummyMeta itemId ts itemText =
|
||||
deletable = False,
|
||||
editable = False,
|
||||
forwardedByMember = Nothing,
|
||||
showGroupAsSender = False,
|
||||
createdAt = ts,
|
||||
updatedAt = ts
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ data ChatMsgEvent (e :: MsgEncoding) where
|
||||
XFileAcptInv :: SharedMsgId -> Maybe ConnReqInvitation -> String -> ChatMsgEvent 'Json
|
||||
XFileCancel :: SharedMsgId -> ChatMsgEvent 'Json
|
||||
XInfo :: Profile -> ChatMsgEvent 'Json
|
||||
XContact :: Profile -> Maybe XContactId -> Maybe MsgContent -> ChatMsgEvent 'Json
|
||||
XContact :: {profile :: Profile, contactReqId :: Maybe XContactId, welcomeMsgId :: Maybe SharedMsgId, requestMsg :: Maybe (SharedMsgId, MsgContent)} -> ChatMsgEvent 'Json
|
||||
XDirectDel :: ChatMsgEvent 'Json
|
||||
XGrpInv :: GroupInvitation -> ChatMsgEvent 'Json
|
||||
XGrpAcpt :: MemberId -> ChatMsgEvent 'Json
|
||||
@@ -1132,7 +1132,14 @@ appJsonToCM AppMessageJson {v, msgId, event, params} = do
|
||||
XFileAcptInv_ -> XFileAcptInv <$> p "msgId" <*> opt "fileConnReq" <*> p "fileName"
|
||||
XFileCancel_ -> XFileCancel <$> p "msgId"
|
||||
XInfo_ -> XInfo <$> p "profile"
|
||||
XContact_ -> XContact <$> p "profile" <*> opt "contactReqId" <*> opt "content"
|
||||
XContact_ -> do
|
||||
profile <- p "profile"
|
||||
contactReqId <- opt "contactReqId"
|
||||
welcomeMsgId <- opt "welcomeMsgId"
|
||||
reqMsgId <- opt "msgId"
|
||||
reqContent <- opt "content"
|
||||
let requestMsg = (,) <$> reqMsgId <*> reqContent
|
||||
pure XContact {profile, contactReqId, welcomeMsgId, requestMsg}
|
||||
XDirectDel_ -> pure XDirectDel
|
||||
XGrpInv_ -> XGrpInv <$> p "groupInvitation"
|
||||
XGrpAcpt_ -> XGrpAcpt <$> p "memberId"
|
||||
@@ -1196,7 +1203,7 @@ chatToAppMessage ChatMessage {chatVRange, msgId, chatMsgEvent} = case encoding @
|
||||
XFileAcptInv sharedMsgId fileConnReq fileName -> o $ ("fileConnReq" .=? fileConnReq) ["msgId" .= sharedMsgId, "fileName" .= fileName]
|
||||
XFileCancel sharedMsgId -> o ["msgId" .= sharedMsgId]
|
||||
XInfo profile -> o ["profile" .= profile]
|
||||
XContact profile xContactId content -> o $ ("contactReqId" .=? xContactId) $ ("content" .=? content) ["profile" .= profile]
|
||||
XContact {profile, contactReqId, welcomeMsgId, requestMsg} -> o $ ("contactReqId" .=? contactReqId) $ ("welcomeMsgId" .=? welcomeMsgId) $ ("msgId" .=? (fst <$> requestMsg)) $ ("content" .=? (snd <$> requestMsg)) $ ["profile" .= profile]
|
||||
XDirectDel -> JM.empty
|
||||
XGrpInv groupInv -> o ["groupInvitation" .= groupInv]
|
||||
XGrpAcpt memId -> o ["memberId" .= memId]
|
||||
|
||||
@@ -8,6 +8,7 @@ module Simplex.Chat.Store
|
||||
UserContactLink (..),
|
||||
GroupLink (..),
|
||||
GroupLinkInfo (..),
|
||||
AddressSettings (..),
|
||||
AutoAccept (..),
|
||||
createChatStore,
|
||||
migrations, -- used in tests
|
||||
|
||||
@@ -110,6 +110,7 @@ import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util ((<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), (:.) (..))
|
||||
@@ -694,20 +695,20 @@ createOrUpdateContactRequest
|
||||
liftIO (maybeM (getAcceptedContactByXContactId db vr user) xContactId_) >>= \case
|
||||
Just ct -> pure $ CORContact ct
|
||||
Nothing -> do
|
||||
(ucr, ct_) <- createOrUpdateRequest
|
||||
pure $ CORRequest ucr ct_
|
||||
(ucr, ct_, newRequest) <- createOrUpdateRequest
|
||||
pure $ CORRequest ucr ct_ newRequest
|
||||
where
|
||||
maybeM = maybe (pure Nothing)
|
||||
createOrUpdateRequest :: ExceptT StoreError IO (UserContactRequest, Maybe Contact)
|
||||
createOrUpdateRequest :: ExceptT StoreError IO (UserContactRequest, Maybe Contact, Bool)
|
||||
createOrUpdateRequest = do
|
||||
cReqId <-
|
||||
(cReqId, newRequest) <-
|
||||
ExceptT $
|
||||
maybeM getContactRequestByXContactId xContactId_ >>= \case
|
||||
Nothing -> createContactRequest
|
||||
Just cr@UserContactRequest {contactRequestId} -> updateContactRequest cr $> Right contactRequestId
|
||||
Nothing -> (,True) <$$> createContactRequest
|
||||
Just cr@UserContactRequest {contactRequestId} -> updateContactRequest cr $> Right (contactRequestId, False)
|
||||
ucr@UserContactRequest {contactId_} <- getContactRequest db user cReqId
|
||||
ct_ <- forM contactId_ $ \contactId -> getContact db vr user contactId
|
||||
pure (ucr, ct_)
|
||||
pure (ucr, ct_, newRequest)
|
||||
createContactRequest :: IO (Either StoreError Int64)
|
||||
createContactRequest = do
|
||||
currentTs <- getCurrentTime
|
||||
|
||||
@@ -31,7 +31,6 @@ module Simplex.Chat.Store.Groups
|
||||
getGroupLinkId,
|
||||
setGroupLinkMemberRole,
|
||||
setGroupLinkShortLink,
|
||||
getGroupAndMember,
|
||||
createNewGroup,
|
||||
createGroupInvitation,
|
||||
deleteContactCardKeepConn,
|
||||
@@ -313,59 +312,6 @@ setGroupLinkShortLink db gLnk@GroupLink {userContactLinkId, connLinkContact = CC
|
||||
(shortLink, BI True, userContactLinkId)
|
||||
pure gLnk {connLinkContact = CCLink connFullLink (Just shortLink), shortLinkDataSet = True}
|
||||
|
||||
getGroupAndMember :: DB.Connection -> User -> Int64 -> VersionRangeChat -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
|
||||
gm <-
|
||||
ExceptT . firstRow toGroupAndMember (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_started_connection,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.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.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
|
||||
-- from GroupMember
|
||||
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.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||
m.created_at, m.updated_at,
|
||||
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
|
||||
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.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|
||||
|]
|
||||
(userId, groupMemberId, userId, userContactId)
|
||||
liftIO $ bitraverse (addGroupChatTags db) pure gm
|
||||
where
|
||||
toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember)
|
||||
toGroupAndMember (groupInfoRow :. memberRow :. connRow) =
|
||||
let groupInfo = toGroupInfo vr userContactId [] groupInfoRow
|
||||
member = toGroupMember userContactId memberRow
|
||||
in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow})
|
||||
|
||||
-- | creates completely new group with a single member - the current user
|
||||
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
|
||||
@@ -579,14 +525,16 @@ deleteContactCardKeepConn db connId Contact {contactId, profile = LocalProfile {
|
||||
DB.execute db "DELETE FROM contacts WHERE contact_id = ?" (Only contactId)
|
||||
DB.execute db "DELETE FROM contact_profiles WHERE contact_profile_id = ?" (Only profileId)
|
||||
|
||||
createPreparedGroup :: DB.Connection -> VersionRangeChat -> User -> GroupProfile -> CreatedLinkContact -> ExceptT StoreError IO GroupInfo
|
||||
createPreparedGroup :: DB.Connection -> VersionRangeChat -> User -> GroupProfile -> CreatedLinkContact -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
createPreparedGroup db vr user@User {userId, userContactId} groupProfile connLinkToConnect = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
(groupId, groupLDN) <- createGroup_ db userId groupProfile (Just connLinkToConnect) Nothing currentTs
|
||||
hostMemberId <- insertHost_ currentTs groupId groupLDN
|
||||
let userMember = MemberIdRole (MemberId $ encodeUtf8 groupLDN <> "_user_unknown_id") GRMember
|
||||
void $ createContactMemberInv_ db user groupId (Just hostMemberId) user userMember GCUserMember GSMemUnknown IBUnknown Nothing currentTs vr
|
||||
getGroupInfo db vr user groupId
|
||||
g <- getGroupInfo db vr user groupId
|
||||
hostMember <- getGroupMember db vr user groupId hostMemberId
|
||||
pure (g, hostMember)
|
||||
where
|
||||
insertHost_ currentTs groupId groupLDN = do
|
||||
let memberId = MemberId $ encodeUtf8 groupLDN <> "_host_unknown_id"
|
||||
|
||||
@@ -496,7 +496,7 @@ setSupportChatTs db groupMemberId chatTs =
|
||||
|
||||
createNewSndChatItem :: DB.Connection -> User -> ChatDirection c 'MDSnd -> SndMessage -> CIContent 'MDSnd -> Maybe (CIQuote c) -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> UTCTime -> IO ChatItemId
|
||||
createNewSndChatItem db user chatDirection SndMessage {msgId, sharedMsgId} ciContent quotedItem itemForwarded timed live createdAt =
|
||||
createNewChatItem_ db user chatDirection createdByMsgId (Just sharedMsgId) ciContent quoteRow itemForwarded timed live False createdAt Nothing createdAt
|
||||
createNewChatItem_ db user chatDirection False createdByMsgId (Just sharedMsgId) ciContent quoteRow itemForwarded timed live False createdAt Nothing createdAt
|
||||
where
|
||||
createdByMsgId = if msgId == 0 then Nothing else Just msgId
|
||||
quoteRow :: NewQuoteRow
|
||||
@@ -512,7 +512,7 @@ createNewSndChatItem db user chatDirection SndMessage {msgId, sharedMsgId} ciCon
|
||||
|
||||
createNewRcvChatItem :: ChatTypeQuotable c => DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> CIContent 'MDRcv -> Maybe CITimed -> Bool -> Bool -> UTCTime -> UTCTime -> IO (ChatItemId, Maybe (CIQuote c), Maybe CIForwardedFrom)
|
||||
createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent, forwardedByMember} sharedMsgId_ ciContent timed live userMention itemTs createdAt = do
|
||||
ciId <- createNewChatItem_ db user chatDirection (Just msgId) sharedMsgId_ ciContent quoteRow itemForwarded timed live userMention itemTs forwardedByMember createdAt
|
||||
ciId <- createNewChatItem_ db user chatDirection False (Just msgId) sharedMsgId_ ciContent quoteRow itemForwarded timed live userMention itemTs forwardedByMember createdAt
|
||||
quotedItem <- mapM (getChatItemQuote_ db user chatDirection) quotedMsg
|
||||
pure (ciId, quotedItem, itemForwarded)
|
||||
where
|
||||
@@ -527,15 +527,15 @@ createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent, forw
|
||||
CDGroupRcv GroupInfo {membership = GroupMember {memberId = userMemberId}} _ _ ->
|
||||
(Just $ Just userMemberId == memberId, memberId)
|
||||
|
||||
createNewChatItemNoMsg :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> CIContent d -> UTCTime -> UTCTime -> IO ChatItemId
|
||||
createNewChatItemNoMsg db user chatDirection ciContent itemTs =
|
||||
createNewChatItem_ db user chatDirection Nothing Nothing ciContent quoteRow Nothing Nothing False False itemTs Nothing
|
||||
createNewChatItemNoMsg :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> ShowGroupAsSender -> CIContent d -> UTCTime -> UTCTime -> IO ChatItemId
|
||||
createNewChatItemNoMsg db user chatDirection showGroupAsSender ciContent itemTs =
|
||||
createNewChatItem_ db user chatDirection showGroupAsSender Nothing Nothing ciContent quoteRow Nothing Nothing False False itemTs Nothing
|
||||
where
|
||||
quoteRow :: NewQuoteRow
|
||||
quoteRow = (Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||
|
||||
createNewChatItem_ :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> Maybe MessageId -> Maybe SharedMsgId -> CIContent d -> NewQuoteRow -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> UTCTime -> Maybe GroupMemberId -> UTCTime -> IO ChatItemId
|
||||
createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent quoteRow itemForwarded timed live userMention itemTs forwardedByMember createdAt = do
|
||||
createNewChatItem_ :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> ShowGroupAsSender -> Maybe MessageId -> Maybe SharedMsgId -> CIContent d -> NewQuoteRow -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> UTCTime -> Maybe GroupMemberId -> UTCTime -> IO ChatItemId
|
||||
createNewChatItem_ db User {userId} chatDirection showGroupAsSender msgId_ sharedMsgId ciContent quoteRow itemForwarded timed live userMention itemTs forwardedByMember createdAt = do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
@@ -544,20 +544,20 @@ createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent q
|
||||
user_id, created_by_msg_id, contact_id, group_id, group_member_id, note_folder_id, group_scope_tag, group_scope_group_member_id,
|
||||
-- meta
|
||||
item_sent, item_ts, item_content, item_content_tag, item_text, item_status, msg_content_tag, shared_msg_id,
|
||||
forwarded_by_group_member_id, include_in_history, created_at, updated_at, item_live, user_mention, timed_ttl, timed_delete_at,
|
||||
forwarded_by_group_member_id, include_in_history, created_at, updated_at, item_live, user_mention, show_group_as_sender, timed_ttl, timed_delete_at,
|
||||
-- quote
|
||||
quoted_shared_msg_id, quoted_sent_at, quoted_content, quoted_sent, quoted_member_id,
|
||||
-- forwarded from
|
||||
fwd_from_tag, fwd_from_chat_name, fwd_from_msg_dir, fwd_from_contact_id, fwd_from_group_id, fwd_from_chat_item_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((userId, msgId_) :. idsRow :. groupScopeRow :. itemRow :. quoteRow' :. forwardedFromRow)
|
||||
ciId <- insertedRowId db
|
||||
forM_ msgId_ $ \msgId -> insertChatItemMessage_ db ciId msgId createdAt
|
||||
pure ciId
|
||||
where
|
||||
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, Text, CIStatus d, Maybe MsgContentTag, Maybe SharedMsgId, Maybe GroupMemberId, BoolInt) :. (UTCTime, UTCTime, Maybe BoolInt, BoolInt) :. (Maybe Int, Maybe UTCTime)
|
||||
itemRow = (msgDirection @d, itemTs, ciContent, toCIContentTag ciContent, ciContentToText ciContent, ciCreateStatus ciContent, msgContentTag <$> ciMsgContent ciContent, sharedMsgId, forwardedByMember, BI includeInHistory) :. (createdAt, createdAt, BI <$> (justTrue live), BI userMention) :. ciTimedRow timed
|
||||
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, Text, CIStatus d, Maybe MsgContentTag, Maybe SharedMsgId, Maybe GroupMemberId, BoolInt) :. (UTCTime, UTCTime, Maybe BoolInt, BoolInt, BoolInt) :. (Maybe Int, Maybe UTCTime)
|
||||
itemRow = (msgDirection @d, itemTs, ciContent, toCIContentTag ciContent, ciContentToText ciContent, ciCreateStatus ciContent, msgContentTag <$> ciMsgContent ciContent, sharedMsgId, forwardedByMember, BI includeInHistory) :. (createdAt, createdAt, BI <$> (justTrue live), BI userMention, BI showGroupAsSender) :. ciTimedRow timed
|
||||
quoteRow' = let (a, b, c, d, e) = quoteRow in (a, b, c, BI <$> d, e)
|
||||
idsRow :: (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId, Maybe NoteFolderId)
|
||||
idsRow = case chatDirection of
|
||||
@@ -1034,7 +1034,7 @@ toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentTex
|
||||
_ -> Just (CIDeleted @'CTLocal deletedTs)
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs Nothing createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs Nothing False createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -2177,7 +2177,7 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT
|
||||
_ -> Just (CIDeleted @'CTDirect deletedTs)
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs Nothing createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs Nothing False createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -2204,7 +2204,7 @@ toGroupChatItem ::
|
||||
UTCTime ->
|
||||
Int64 ->
|
||||
ChatItemRow
|
||||
:. Only (Maybe GroupMemberId)
|
||||
:. (Maybe GroupMemberId, BoolInt)
|
||||
:. MaybeGroupMemberRow
|
||||
:. GroupQuoteRow
|
||||
:. MaybeGroupMemberRow ->
|
||||
@@ -2218,7 +2218,7 @@ toGroupChatItem
|
||||
:. (timedTTL, timedDeleteAt, itemLive, BI userMention)
|
||||
:. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)
|
||||
)
|
||||
:. Only forwardedByMember
|
||||
:. (forwardedByMember, BI showGroupAsSender)
|
||||
:. memberRow_
|
||||
:. (quoteRow :. quotedMemberRow_)
|
||||
:. deletedByGroupMemberRow_
|
||||
@@ -2260,7 +2260,7 @@ toGroupChatItem
|
||||
_ -> Just (maybe (CIDeleted @'CTGroup deletedTs) (CIModerated deletedTs) deletedByGroupMember_)
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs forwardedByMember createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) userMention currentTs itemTs forwardedByMember showGroupAsSender createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -2889,8 +2889,8 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
||||
i.timed_ttl, i.timed_delete_at, i.item_live, i.user_mention,
|
||||
-- CIFile
|
||||
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
||||
-- CIMeta forwardedByMember
|
||||
i.forwarded_by_group_member_id,
|
||||
-- CIMeta forwardedByMember, showGroupAsSender
|
||||
i.forwarded_by_group_member_id, i.show_group_as_sender,
|
||||
-- GroupMember
|
||||
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.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
module Simplex.Chat.Store.Profiles
|
||||
( AutoAccept (..),
|
||||
AddressSettings (..),
|
||||
UserMsgReceiptSettings (..),
|
||||
UserContactLink (..),
|
||||
GroupLinkInfo (..),
|
||||
@@ -54,7 +55,7 @@ module Simplex.Chat.Store.Profiles
|
||||
setUserContactLinkShortLink,
|
||||
getContactWithoutConnViaAddress,
|
||||
getContactWithoutConnViaShortAddress,
|
||||
updateUserAddressAutoAccept,
|
||||
updateUserAddressSettings,
|
||||
getProtocolServers,
|
||||
insertProtocolServer,
|
||||
getUpdateServerOperators,
|
||||
@@ -451,7 +452,7 @@ data UserContactLink = UserContactLink
|
||||
{ userContactLinkId :: Int64,
|
||||
connLinkContact :: CreatedLinkContact,
|
||||
shortLinkDataSet :: Bool,
|
||||
autoAccept :: Maybe AutoAccept
|
||||
addressSettings :: AddressSettings
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
@@ -464,21 +465,30 @@ data GroupLinkInfo = GroupLinkInfo
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data AutoAccept = AutoAccept
|
||||
data AddressSettings = AddressSettings
|
||||
{ businessAddress :: Bool, -- possibly, it can be wrapped together with acceptIncognito, or AutoAccept made sum type
|
||||
acceptIncognito :: IncognitoEnabled,
|
||||
autoReply :: Maybe MsgContent
|
||||
welcomeMessage :: Maybe Text, -- included in short link information
|
||||
autoAccept :: Maybe AutoAccept, -- accept automatically
|
||||
autoReply :: Maybe MsgContent -- sent on acceptance, can be supported with manual acceptance as well
|
||||
}
|
||||
deriving (Show)
|
||||
deriving (Eq, Show)
|
||||
|
||||
data AutoAccept = AutoAccept
|
||||
{ acceptIncognito :: IncognitoEnabled -- "incognito" is allowed onle for old addresses without short link data
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''AutoAccept)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''AddressSettings)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''UserContactLink)
|
||||
|
||||
toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI autoAccept, BI businessAddress, BI acceptIncognito, autoReply) =
|
||||
toUserContactLink :: (Int64, ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, Maybe Text, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (userContactLinkId, connReq, shortLink, BI shortLinkDataSet, BI businessAddress, welcomeMessage, BI autoAccept', BI acceptIncognito, autoReply) =
|
||||
UserContactLink userContactLinkId (CCLink connReq shortLink) shortLinkDataSet $
|
||||
if autoAccept then Just AutoAccept {businessAddress, acceptIncognito, autoReply} else Nothing
|
||||
let autoAccept = if autoAccept' then Just AutoAccept {acceptIncognito} else Nothing
|
||||
in AddressSettings {businessAddress, welcomeMessage, autoAccept, autoReply}
|
||||
|
||||
getUserAddress :: DB.Connection -> User -> ExceptT StoreError IO UserContactLink
|
||||
getUserAddress db User {userId} =
|
||||
@@ -491,7 +501,7 @@ getUserContactLinkById db userId userContactLinkId =
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, 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, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|]
|
||||
@@ -527,7 +537,7 @@ getUserContactLinkViaShortLink db User {userId} shortLink =
|
||||
userContactLinkQuery :: Query
|
||||
userContactLinkQuery =
|
||||
[sql|
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
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
|
||||
FROM user_contact_links
|
||||
|]
|
||||
|
||||
@@ -576,20 +586,20 @@ getContactWithoutConnViaShortAddress db vr user@User {userId} shortLink = do
|
||||
(userId, shortLink)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) ctId_
|
||||
|
||||
updateUserAddressAutoAccept :: DB.Connection -> Int64 -> Maybe AutoAccept -> IO ()
|
||||
updateUserAddressAutoAccept db userContactLinkId autoAccept =
|
||||
updateUserAddressSettings :: DB.Connection -> Int64 -> AddressSettings -> IO ()
|
||||
updateUserAddressSettings db userContactLinkId AddressSettings {businessAddress, welcomeMessage, autoAccept, autoReply} =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?, business_address = ?, auto_accept_incognito = ?, auto_reply_msg_content = ?
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, address_welcome_message = ?, auto_reply_msg_content = ?
|
||||
WHERE user_contact_link_id = ?
|
||||
|]
|
||||
(autoAcceptValues :. Only userContactLinkId)
|
||||
(autoAcceptValues :. (businessAddress, welcomeMessage, autoReply, userContactLinkId))
|
||||
where
|
||||
autoAcceptValues = case autoAccept of
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply} -> (BI True, BI businessAddress, BI acceptIncognito, autoReply)
|
||||
_ -> (BI False, BI False, BI False, Nothing)
|
||||
Just AutoAccept {acceptIncognito} -> (BI True, BI acceptIncognito)
|
||||
Nothing -> (BI False, BI False)
|
||||
|
||||
getProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> IO [UserServer p]
|
||||
getProtocolServers db p User {userId} =
|
||||
|
||||
@@ -15,10 +15,13 @@ ALTER TABLE contacts ADD COLUMN contact_request_id INTEGER REFERENCES contact_re
|
||||
CREATE INDEX idx_contacts_contact_request_id ON contacts(contact_request_id);
|
||||
|
||||
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_short_link_to_connect BLOB;
|
||||
ALTER TABLE groups ADD COLUMN conn_link_started_connection INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN show_group_as_sender INTEGER NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20250526_short_links :: Query
|
||||
@@ -31,8 +34,11 @@ DROP INDEX idx_contacts_contact_request_id;
|
||||
ALTER TABLE contacts DROP COLUMN contact_request_id;
|
||||
|
||||
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_short_link_to_connect;
|
||||
ALTER TABLE groups DROP COLUMN conn_link_started_connection;
|
||||
|
||||
ALTER TABLE chat_items DROP COLUMN show_group_as_sender;
|
||||
|]
|
||||
|
||||
@@ -822,8 +822,8 @@ Query:
|
||||
i.timed_ttl, i.timed_delete_at, i.item_live, i.user_mention,
|
||||
-- CIFile
|
||||
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
||||
-- CIMeta forwardedByMember
|
||||
i.forwarded_by_group_member_id,
|
||||
-- CIMeta forwardedByMember, showGroupAsSender
|
||||
i.forwarded_by_group_member_id, i.show_group_as_sender,
|
||||
-- GroupMember
|
||||
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.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||
@@ -3496,7 +3496,7 @@ Plan:
|
||||
SEARCH usage_conditions USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, 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, address_welcome_message, auto_accept, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|
||||
@@ -4123,12 +4123,12 @@ Query:
|
||||
user_id, created_by_msg_id, contact_id, group_id, group_member_id, note_folder_id, group_scope_tag, group_scope_group_member_id,
|
||||
-- meta
|
||||
item_sent, item_ts, item_content, item_content_tag, item_text, item_status, msg_content_tag, shared_msg_id,
|
||||
forwarded_by_group_member_id, include_in_history, created_at, updated_at, item_live, user_mention, timed_ttl, timed_delete_at,
|
||||
forwarded_by_group_member_id, include_in_history, created_at, updated_at, item_live, user_mention, show_group_as_sender, timed_ttl, timed_delete_at,
|
||||
-- quote
|
||||
quoted_shared_msg_id, quoted_sent_at, quoted_content, quoted_sent, quoted_member_id,
|
||||
-- forwarded from
|
||||
fwd_from_tag, fwd_from_chat_name, fwd_from_msg_dir, fwd_from_contact_id, fwd_from_group_id, fwd_from_chat_item_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -4639,7 +4639,7 @@ SEARCH server_operators USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?, business_address = ?, auto_accept_incognito = ?, auto_reply_msg_content = ?
|
||||
SET auto_accept = ?, auto_accept_incognito = ?, business_address = ?, address_welcome_message = ?, auto_reply_msg_content = ?
|
||||
WHERE user_contact_link_id = ?
|
||||
|
||||
Plan:
|
||||
@@ -5190,21 +5190,21 @@ Plan:
|
||||
SCAN usage_conditions
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
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
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND conn_req_contact IN (?,?)
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
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
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
|
||||
Query:
|
||||
SELECT user_contact_link_id, conn_req_contact, short_link_contact, short_link_data_set, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
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
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND short_link_contact = ?
|
||||
Plan:
|
||||
|
||||
@@ -333,6 +333,7 @@ CREATE TABLE user_contact_links(
|
||||
business_address INTEGER DEFAULT 0,
|
||||
short_link_contact BLOB,
|
||||
short_link_data_set INTEGER NOT NULL DEFAULT 0,
|
||||
address_welcome_message TEXT,
|
||||
UNIQUE(user_id, local_display_name)
|
||||
);
|
||||
CREATE TABLE contact_requests(
|
||||
@@ -426,7 +427,8 @@ CREATE TABLE chat_items(
|
||||
include_in_history INTEGER NOT NULL DEFAULT 0,
|
||||
user_mention INTEGER NOT NULL DEFAULT 0,
|
||||
group_scope_tag TEXT,
|
||||
group_scope_group_member_id INTEGER REFERENCES group_members(group_member_id) ON DELETE CASCADE
|
||||
group_scope_group_member_id INTEGER REFERENCES group_members(group_member_id) ON DELETE CASCADE,
|
||||
show_group_as_sender INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE TABLE sqlite_sequence(name,seq);
|
||||
CREATE TABLE chat_item_messages(
|
||||
|
||||
@@ -200,6 +200,9 @@ data Contact = Contact
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
contactRequestId' :: Contact -> Maybe Int64
|
||||
contactRequestId' Contact {contactRequestId} = contactRequestId
|
||||
|
||||
data PreparedContact = PreparedContact {connLinkToConnect :: ACreatedConnLink, uiConnLinkType :: ConnectionMode}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -368,10 +371,15 @@ instance ToJSON ConnReqUriHash where
|
||||
toJSON = strToJSON
|
||||
toEncoding = strToJEncoding
|
||||
|
||||
-- TODO [short links] this type is most likely incorrect, as it does not communicate when contact exists as opposed to when it is
|
||||
-- just created, as was the original intention.
|
||||
-- It also has no information when group exists on repeat requests.
|
||||
-- Most likely, whatever information from request is needed should have been added to CORContact (or inside Contact),
|
||||
-- instead of passing Maybe contact in request.
|
||||
data ChatOrRequest
|
||||
= CORContact Contact
|
||||
-- Contact is Maybe for backward compatibility with legacy requests, all new requests are created with contact
|
||||
| CORRequest UserContactRequest (Maybe Contact)
|
||||
| CORRequest UserContactRequest (Maybe Contact) Bool
|
||||
|
||||
type UserName = Text
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ import Simplex.Chat.Operators
|
||||
import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Remote.AppVersion (AppVersion (..), pattern AppVersionRange)
|
||||
import Simplex.Chat.Remote.Types
|
||||
import Simplex.Chat.Store (AutoAccept (..), GroupLink (..), StoreError (..), UserContactLink (..))
|
||||
import Simplex.Chat.Store (AddressSettings (..), AutoAccept (..), GroupLink (..), StoreError (..), UserContactLink (..))
|
||||
import Simplex.Chat.Styled
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
@@ -170,8 +170,8 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
HSDatabase -> databaseHelpInfo
|
||||
CRWelcome user -> chatWelcome user
|
||||
CRContactsList u cs -> ttyUser u $ viewContactsList cs
|
||||
CRUserContactLink u UserContactLink {connLinkContact, autoAccept} -> ttyUser u $ connReqContact_ "Your chat address:" connLinkContact <> autoAcceptStatus_ autoAccept
|
||||
CRUserContactLinkUpdated u UserContactLink {autoAccept} -> ttyUser u $ autoAcceptStatus_ autoAccept
|
||||
CRUserContactLink u UserContactLink {connLinkContact, addressSettings} -> ttyUser u $ connReqContact_ "Your chat address:" connLinkContact <> viewAddressSettings addressSettings
|
||||
CRUserContactLinkUpdated u UserContactLink {addressSettings} -> ttyUser u $ viewAddressSettings addressSettings
|
||||
CRContactRequestRejected u UserContactRequest {localDisplayName = c} _ct_ -> ttyUser u [ttyContact c <> ": contact request rejected"]
|
||||
CRGroupCreated u g -> ttyUser u $ viewGroupCreated g testView
|
||||
CRGroupMembers u g -> ttyUser u $ viewGroupMembers g
|
||||
@@ -190,8 +190,10 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRConnectionIncognitoUpdated u c customUserProfile -> ttyUser u $ viewConnectionIncognitoUpdated c customUserProfile testView
|
||||
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged u c nu c'
|
||||
CRConnectionPlan u connLink connectionPlan -> ttyUser u $ viewConnectionPlan cfg connLink connectionPlan
|
||||
CRNewPreparedContact u c -> ttyUser u [ttyContact' c <> ": contact is prepared"]
|
||||
CRNewPreparedGroup u g -> ttyUser u [ttyGroup' g <> ": group is prepared"]
|
||||
CRNewPreparedChat u (AChat _ (Chat cInfo _ _)) -> ttyUser u $ case cInfo of
|
||||
DirectChat ct -> [ttyContact' ct <> ": contact is prepared"]
|
||||
GroupChat g _ -> [ttyGroup' g <> ": group is prepared"]
|
||||
_ -> ["prepared chat error: unexpected type"]
|
||||
CRContactUserChanged u c nu c' -> ttyUser u $ viewContactUserChanged u c nu c'
|
||||
CRGroupUserChanged u g nu g' -> ttyUser u $ viewGroupUserChanged u g nu g'
|
||||
CRSentConfirmation u _ _customUserProfile -> ttyUser u ["confirmation sent!"]
|
||||
@@ -418,7 +420,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
CEvtContactUpdated {user = u, fromContact = c, toContact = c'} -> ttyUser u $ viewContactUpdated c c' <> viewContactPrefsUpdated u c c'
|
||||
CEvtGroupMemberUpdated {} -> []
|
||||
CEvtContactsMerged u intoCt mergedCt ct' -> ttyUser u $ viewContactsMerged intoCt mergedCt ct'
|
||||
CEvtReceivedContactRequest u UserContactRequest {localDisplayName = c, profile} _ct_ -> ttyUser u $ viewReceivedContactRequest c profile
|
||||
CEvtReceivedContactRequest u UserContactRequest {localDisplayName = c, profile} _chat -> ttyUser u $ viewReceivedContactRequest c profile
|
||||
CEvtRcvFileStart u ci -> ttyUser u $ receivingFile_' hu testView "started" ci
|
||||
CEvtRcvFileComplete u ci -> ttyUser u $ receivingFile_' hu testView "completed" ci
|
||||
CEvtRcvStandaloneFileComplete u _ ft -> ttyUser u $ receivingFileStandalone "completed" ft
|
||||
@@ -1080,9 +1082,10 @@ simplexChatContact' = \case
|
||||
CLFull (CRContactUri crData) -> CLFull $ CRContactUri crData {crScheme = simplexChat}
|
||||
l@(CLShort _) -> l
|
||||
|
||||
autoAcceptStatus_ :: Maybe AutoAccept -> [StyledString]
|
||||
autoAcceptStatus_ = \case
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply} ->
|
||||
-- TODO [short links] show all settings
|
||||
viewAddressSettings :: AddressSettings -> [StyledString]
|
||||
viewAddressSettings AddressSettings {businessAddress, welcomeMessage = _, autoAccept, autoReply} = case autoAccept of
|
||||
Just AutoAccept {acceptIncognito} ->
|
||||
("auto_accept on" <> aaInfo)
|
||||
: maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user