Merge remote-tracking branch 'origin/master' into ab/names-lookup-api

This commit is contained in:
Alain Brenzikofer
2026-09-24 15:36:32 +02:00
34 changed files with 521 additions and 490 deletions
+1 -1
View File
@@ -2500,7 +2500,7 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
}
public var isContactCard: Bool {
(activeConn == nil || activeConn?.connStatus == .prepared) && profile.contactLink != nil && active && preparedContact == nil && contactRequestId == nil
(activeConn == nil || activeConn?.connStatus == .prepared) && profile.contactLink != nil && active && preparedContact == nil && contactRequestId == nil && groupDirectInv == nil
}
@inline(__always)
@@ -1931,7 +1931,7 @@ data class Contact(
}
val isContactCard: Boolean get() =
(activeConn == null || activeConn.connStatus == ConnStatus.Prepared) && profile.contactLink != null && active && preparedContact == null && contactRequestId == null
(activeConn == null || activeConn.connStatus == ConnStatus.Prepared) && profile.contactLink != null && active && preparedContact == null && contactRequestId == null && groupDirectInv == null
val isBot: Boolean get() = profile.peerType == ChatPeerType.Bot
+12 -8
View File
@@ -20,7 +20,7 @@ import Data.Char (isUpper, toLower, toUpper)
import Data.List (find, mapAccumL, sortOn)
import qualified Data.List.NonEmpty as L
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)
import Data.Maybe (fromMaybe, mapMaybe)
import qualified Data.Set as S
import Data.Text (Text)
import GHC.Generics
@@ -177,11 +177,15 @@ normalizeConsName pfx consName
-- making chatDir optional because clients use CIDirection? instead of CIQDirection (the type is replaced in Types.hs)
ciQuoteType :: SumTypeInfo
ciQuoteType =
let st@(STI _ records) = sti @(CIQuote 'CTDirect)
optChatDir f@(FieldInfo n t) = if n == "chatDir" then FieldInfo n (TIOptional t) else f
updateRecord (RecordTypeInfo name fields) = RecordTypeInfo name $ map optChatDir fields
in st {recordTypes = map updateRecord records} -- need to map even though there is one constructor in this type
ciQuoteType = updateFields mkOptional $ sti @(CIQuote 'CTDirect)
where
mkOptional = map (\f@(FieldInfo n t) -> if n == "chatDir" then FieldInfo n (TIOptional t) else f)
removeField :: String -> SumTypeInfo -> SumTypeInfo
removeField n = updateFields $ mapMaybe (\f@(FieldInfo n' _) -> if n == n' then Nothing else Just f)
updateFields :: ([FieldInfo] -> [FieldInfo]) -> SumTypeInfo -> SumTypeInfo
updateFields f st@(STI _ records) = st {recordTypes = map (\(RecordTypeInfo name fields) -> RecordTypeInfo name $ f fields) records}
-- JSON field names drop the "nr" prefix, as NameRecord's deriveJSON does
nameRecordType :: SumTypeInfo
@@ -310,7 +314,7 @@ chatTypesDocsData =
(sti @GroupMemberSettings, STRecord, "", [], "", ""),
(sti @GroupMemberStatus, STEnum' ((\case "group_deleted" -> "deleted"; "intro_invited" -> "intro-inv"; s -> s) . consSep "GSMem" '_'), "", [], "", ""),
(sti @GroupPreference, STRecord, "", [], "", ""),
(sti @GroupPreferences, STRecord, "", [], "", ""),
(removeField "_json" $ sti @GroupPreferences, STRecord, "", [], "", ""),
(sti @GroupProfile, STRecord, "", [], "", ""),
(sti @GroupRelay, STRecord, "", [], "", ""),
(sti @GroupShortLinkData, STRecord, "", [], "", ""),
@@ -350,7 +354,7 @@ chatTypesDocsData =
(sti @PendingContactConnection, STRecord, "", [], "", ""),
(sti @PlanResolveMode, STEnum, "PRM", [], "", ""),
(sti @PrefEnabled, STRecord, "", [], "", ""),
(sti @Preferences, STRecord, "", [], "", ""),
(removeField "_json" $ sti @Preferences, STRecord, "", [], "", ""),
(sti @PreparedContact, STRecord, "", [], "", ""),
(sti @GroupDirectInvitation, STRecord, "", [], "", ""),
(sti @PreparedGroup, STRecord, "", [], "", ""),
+1 -1
View File
@@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 900c45ffaee5eb7eef8ec9402bc4971e4eb7ef33
tag: 5294b7d8b7285a887b849b738e29bec207e5dde8
source-repository-package
type: git
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."900c45ffaee5eb7eef8ec9402bc4971e4eb7ef33" = "1iygb9hkc86ky553jg5apc8vfbjj0lghjaywikc0m7h1yk7zibxg";
"https://github.com/simplex-chat/simplexmq.git"."5294b7d8b7285a887b849b738e29bec207e5dde8" = "0adjvg6mcyk936qzyl3a4daq68kslzpqhpdh369cf8wwcfqkiqg4";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
+2
View File
@@ -167,6 +167,7 @@ library
Simplex.Chat.Store.Postgres.Migrations.M20260904_file_badges
Simplex.Chat.Store.Postgres.Migrations.M20260915_user_badges
Simplex.Chat.Store.Postgres.Migrations.M20260918_badge_issue_errors
Simplex.Chat.Store.Postgres.Migrations.M20260923_preferences_json
Simplex.Chat.Store.Postgres.Migrations.M20260924_simplex_name_resolved
else
exposed-modules:
@@ -344,6 +345,7 @@ library
Simplex.Chat.Store.SQLite.Migrations.M20260904_file_badges
Simplex.Chat.Store.SQLite.Migrations.M20260915_user_badges
Simplex.Chat.Store.SQLite.Migrations.M20260918_badge_issue_errors
Simplex.Chat.Store.SQLite.Migrations.M20260923_preferences_json
Simplex.Chat.Store.SQLite.Migrations.M20260924_simplex_name_resolved
other-modules:
Paths_simplex_chat
+2 -1
View File
@@ -42,6 +42,7 @@ import System.Exit (exitFailure)
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
import UnliftIO.Async
import UnliftIO.Exception (finally)
simplexChatCore :: ChatConfig -> ChatOpts -> (User -> ChatController -> IO ()) -> IO ()
simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@ChatOpts {coreOptions = coreOptions@CoreChatOpts {dbOptions, logAgent, yesToUpMigrations, migrationBackupPath, maintenance}, createBot, userDisplayName, userImageFile} chat =
@@ -89,7 +90,7 @@ simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@Cha
runSimplexChat :: ChatConfig -> ChatOpts -> User -> ChatController -> (User -> ChatController -> IO ()) -> IO ()
runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay, chatRelayServer, headless, maintenance}} u cc@ChatController {config = ChatConfig {chatHooks}} chat
| maintenance = wait =<< async (chat u cc)
| otherwise = do
| otherwise = flip finally (stopChatController cc) $ do
a1 <- runReaderT (startChatController True True False) cc
when (chatRelay && not testView) $ askCreateRelayAddress cc u chatRelayServer headless
forM_ (postStartHook chatHooks) ($ cc)
+16 -15
View File
@@ -1722,7 +1722,7 @@ processChatCommand cxt nm = \case
Just _ -> do
let chatV = initialChatVersion
subMode <- chatReadVar subscriptionMode
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(connId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
conn@Connection {connId = testCId} <- withFastStore $ \db ->
createRelayTestConnection db cxt user connId ConnPrepared chatV subMode
challenge <- drgRandomBytes 32
@@ -2427,7 +2427,8 @@ processChatCommand cxt nm = \case
g' <- withFastStore' $ \db -> setGroupDomainVerified db user g verified expiresAt
pure $ CRGroupDomainVerified user g' reason
APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do
ct@Contact {profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db cxt user contactId
ct@Contact {profile = LocalProfile {contactLink}, groupDirectInv} <- withFastStore $ \db -> getContact db cxt user contactId
when (isJust groupDirectInv) $ throwCmdError "contact is a member contact request"
ccLink <- case contactLink of
Just (CLFull cReq) -> pure $ CCLink cReq Nothing
Just (CLShort sLnk) -> do
@@ -2845,7 +2846,7 @@ processChatCommand cxt nm = \case
dm <- encodeConnInfo $ XGrpAcpt membershipMemId (Just $ groupMemberKey gks)
agentConnId <- case memberConn fromMember of
Nothing -> do
agentConnId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connRequest PQSupportOff
(agentConnId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connRequest PQSupportOff
let chatV = vr cxt `peerConnChatVersion` peerChatVRange
void $ withFastStore' $ \db -> createMemberConnection db userId fromMember agentConnId chatV peerChatVRange subMode
pure agentConnId
@@ -3399,7 +3400,7 @@ processChatCommand cxt nm = \case
-- possible improvement: use agent connRequestAgentVersion to determine pqSupport here;
-- for joinPreparedConn below - same + encodeConnInfoPQ;
-- same for auto-accept on xGrpDirectInv
acId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(acId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
conn <- withStore $ \db -> do
connId <- liftIO $ createMemberContactConn db user acId Nothing gInfo mConn ConnPrepared contactId subMode
getConnectionById db cxt user connId
@@ -3758,7 +3759,7 @@ processChatCommand cxt nm = \case
withMemberName gName mName cmd = withUser $ \user ->
getGroupAndMemberId user gName mName >>= processChatCommand cxt nm . uncurry cmd
getConnectionCode :: ConnId -> CM Text
getConnectionCode connId = verificationCode <$> withAgent (`getConnectionRatchetAdHash` connId)
getConnectionCode connId = verificationCode . codeAD <$> withAgent (`getConnectionVerifyCodes` connId)
getChannelMemberCode :: GroupInfo -> GroupMember -> CM Text
getChannelMemberCode GroupInfo {membership} m =
case (memberPubKey membership, memberPubKey m) of
@@ -3819,7 +3820,7 @@ processChatCommand cxt nm = \case
joinNewConn chatV = do
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
(connId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
conn <- withFastStore' $ \db -> createDirectConnection' db userId connId ccLink contactId_ ConnPrepared incognitoProfile subMode chatV pqSup'
joinPreparedConn conn incognitoProfile
@@ -3874,13 +3875,13 @@ processChatCommand cxt nm = \case
relayMemberId_ = case preparedEntity_ of
Just (PCEGroup (GIK gInfo _) m) | useRelays' gInfo -> Just (memberId' m)
_ -> Nothing
joinPreparedConn' xContactId_ conn@Connection {customUserProfileId} gInfo_ = do
joinPreparedConn' xContactId_ conn@Connection {connId, customUserProfileId} gInfo_ = do
when (incognito /= isJust customUserProfileId) $ throwCmdError "incognito mode is different from prepared connection"
-- TODO [relays] member: refactor joinContact and up avoiding parallel ifs, xContactId is not used
xContactId <- mkXContactId xContactId_
localIncognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
(cReq', localIncognitoProfile) <- withFastStore $ \db -> (,) <$> getConnReqContact db connId <*> forM customUserProfileId (getProfileById db userId)
let incognitoProfile = fromLocalProfile <$> localIncognitoProfile
conn' <- joinContact user conn cReq incognitoProfile xContactId welcomeSharedMsgId msg_ gInfo_ relayMemberId_ PQSupportOn
conn' <- joinContact user conn cReq' incognitoProfile xContactId welcomeSharedMsgId msg_ gInfo_ relayMemberId_ PQSupportOn
pure $ CVRSentInvitation conn' incognitoProfile
connect' groupLinkId xContactId_ gInfo_ = do
let inGroup = isJust groupLinkId
@@ -3913,13 +3914,13 @@ processChatCommand cxt nm = \case
void $ joinContact user conn cReq incognitoProfile newXContactId Nothing Nothing Nothing Nothing pqSup
ct' <- withStore $ \db -> getContact db cxt user contactId
pure $ CRSentInvitationToContact user ct' incognitoProfile
Just conn@Connection {connStatus, xContactId = xContactId_, customUserProfileId} -> case connStatus of
Just conn@Connection {connId, connStatus, xContactId = xContactId_, customUserProfileId} -> case connStatus of
ConnPrepared -> do
when (incognito /= isJust customUserProfileId) $ throwCmdError "incognito mode is different from prepared connection"
xContactId <- mkXContactId xContactId_
localIncognitoProfile <- forM customUserProfileId $ \pId -> withFastStore $ \db -> getProfileById db userId pId
(cReq', localIncognitoProfile) <- withFastStore $ \db -> (,) <$> getConnReqContact db connId <*> forM customUserProfileId (getProfileById db userId)
let incognitoProfile = fromLocalProfile <$> localIncognitoProfile
void $ joinContact user conn cReq incognitoProfile xContactId Nothing Nothing Nothing Nothing PQSupportOn
void $ joinContact user conn cReq' incognitoProfile xContactId Nothing Nothing Nothing Nothing PQSupportOn
ct' <- withStore $ \db -> getContact db cxt user contactId
pure $ CRSentInvitationToContact user ct' incognitoProfile
_ -> throwCmdError "contact already has connection"
@@ -3966,7 +3967,7 @@ processChatCommand cxt nm = \case
Nothing -> throwChatError CEInvalidConnReq
Just _ -> do
let chatV = initialChatVersion
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup
(connId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup
pure (connId, chatV)
mkXContactId :: Maybe XContactId -> CM XContactId
mkXContactId = maybe (XContactId <$> drgRandomBytes 16) pure
@@ -4305,7 +4306,7 @@ processChatCommand cxt nm = \case
let chatV = initialChatVersion
gVar <- asks random
subMode <- chatReadVar subscriptionMode
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(connId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(relayMember, conn, groupRelay) <- withFastStore $ \db -> do
relayMember <- createRelayForOwner db cxt gVar user gInfo relay
groupRelay <- createGroupRelayRecord db gInfo relayMember relay
@@ -4761,7 +4762,7 @@ processChatCommand cxt nm = \case
SRDirect contactId -> do
ct <- withFastStore $ \db -> getContact db cxt u contactId
forM (contactConn ct) $ \conn ->
(CBDirect,) <$> withAgent (`getConnectionRatchetAdHash` aConnId conn)
(CBDirect,) . codeAD <$> withAgent (`getConnectionVerifyCodes` aConnId conn)
SRGroup toGroupId _ asGroup -> do
GroupInfo {groupProfile = GroupProfile {publicGroup}, membership = m} <- withFastStore $ \db -> getGroupInfo db cxt u toGroupId
pure $ mkBinding m <$> publicGroup
+5 -5
View File
@@ -977,7 +977,7 @@ acceptContactRequest nm user@User {userId} UserContactRequest {agentInvitationId
(ct, conn, incognitoProfile) <- case contactId_ of
Nothing -> do
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
(connId, _) <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
(ct, conn) <- withStore' $ \db ->
createContactFromRequest db user userContactLinkId_ connId chatV cReqChatVRange cName profileId cp xContactId incognitoProfile subMode pqSup' False
pure (ct, conn, incognitoProfile)
@@ -986,7 +986,7 @@ acceptContactRequest nm user@User {userId} UserContactRequest {agentInvitationId
case contactConn ct of
Nothing -> do
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
(connId, _) <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
currentTs <- liftIO getCurrentTime
conn <- withStore' $ \db -> do
forM_ xContactId $ \xcId -> setContactAcceptedXContactId db ct xcId
@@ -2328,7 +2328,7 @@ type HistoryFile = (FileInvitation, RcvFileDescrText, Maybe UTCTime, Maybe Badge
directChatBinding :: Contact -> CM (Maybe ByteString)
directChatBinding ct =
forM (contactConn ct) $ \conn ->
encodeChatBinding CBDirect <$> withAgent (`getConnectionRatchetAdHash` aConnId conn)
encodeChatBinding CBDirect . codeAD <$> withAgent (`getConnectionVerifyCodes` aConnId conn)
rcvGroupChatBinding :: GroupInfo -> Maybe GroupMember -> ShowGroupAsSender -> Maybe BadgeProof -> Maybe ByteString
rcvGroupChatBinding gInfo m_ asGroup badge_ =
@@ -3028,7 +3028,7 @@ prepareAgentJoin user conn_ enableNtfs cReqUri = do
cmdId <- withStore' $ \db -> createCommand db user (dbConnId <$> conn_) CFJoinConn
connId <- case conn_ of
Just conn -> pure $ aConnId conn
Nothing -> withAgent $ \a -> prepareConnectionToJoin a (aUserId user) enableNtfs cReqUri PQSupportOff
Nothing -> fst <$> withAgent (\a -> prepareConnectionToJoin a (aUserId user) enableNtfs cReqUri PQSupportOff)
pure (cmdId, connId)
joinAgentConnectionAsync :: CommandId -> Bool -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> SubscriptionMode -> CM ()
@@ -3054,7 +3054,7 @@ allowAgentConnectionInfo user conn@Connection {connId} confId dm = do
prepareAgentAccept :: User -> Bool -> InvitationId -> PQSupport -> CM (CommandId, ConnId)
prepareAgentAccept user enableNtfs invId pqSup = do
cmdId <- withStore' $ \db -> createCommand db user Nothing CFAcceptContact
connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) enableNtfs invId pqSup
(connId, _) <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) enableNtfs invId pqSup
pure (cmdId, connId)
agentAcceptContactAsync :: MsgEncodingI e => CommandId -> ConnId -> Bool -> InvitationId -> ChatMsgEvent e -> PQSupport -> SubscriptionMode -> CM ()
+4 -4
View File
@@ -1390,7 +1390,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
processContactConnMessage :: AEvent e -> ConnectionEntity -> Connection -> UserContact -> CM ()
processContactConnMessage agentMsg connEntity conn UserContact {userContactLinkId = uclId, groupId = ucGroupId_} = case agentMsg of
REQ invId pqSupport _ connInfo rejectionSupported -> do
REQ invId pqSupport _ connInfo _ rejectionSupported -> do
(signedMsg_, ChatMessage {chatVRange, chatMsgEvent}) <- parseChatMessage' conn connInfo
case chatMsgEvent of
XContact p memberKey_ xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p memberKey_ xContactId_ welcomeMsgId_ requestMsg_ pqSupport rejectionSupported
@@ -1908,7 +1908,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
keepSig <- case contactConn ct of
Nothing -> pure False
Just conn -> do
adHash <- withAgent (`getConnectionRatchetAdHash` aConnId conn)
adHash <- codeAD <$> withAgent (`getConnectionVerifyCodes` aConnId conn)
pure $ encodeChatBinding CBDirect adHash == binding
pure $ if keepSig then c else MCChat {text, chatLink, ownerSig = Nothing}
_ -> pure c
@@ -3877,7 +3877,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
securityCodeChanged mCt'
createItems mCt' m
| otherwise = do
acId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connReq PQSupportOff
(acId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connReq PQSupportOff
mCt' <- withStore $ \db -> do
updateMemberContactInvited db user mCt groupDirectInv
void $ liftIO $ createMemberContactConn db user acId Nothing g mConn ConnPrepared mContactId subMode
@@ -3898,7 +3898,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
createInternalChatItem user (CDDirectSnd mCt) CIChatBanner (Just epochStart)
createItems mCt m'
| otherwise = do
acId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connReq PQSupportOff
(acId, _) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connReq PQSupportOff
(mCt, m') <- withStore $ \db -> do
(mContactId, m') <- liftIO $ createMemberContactInvited db user g m groupDirectInv
void $ liftIO $ createMemberContactConn db user acId Nothing g mConn ConnPrepared mContactId subMode
+8 -6
View File
@@ -37,6 +37,7 @@ import Simplex.Chat.Store.Direct
import Simplex.Chat.Store.Groups
import Simplex.Chat.Store.Shared
import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Protocol (ConnId)
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', fromOnlyBI, maybeFirstRow)
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
@@ -119,7 +120,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
[sql|
SELECT
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, 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.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
p.preferences, p.preferences_json, 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, cr2.rejection_supported,
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
@@ -131,8 +132,9 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
|]
(userId, contactId, CSActive)
toContact' :: UTCTime -> Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) =
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge currentTs badgeRow, preferences, localAlias}
toContact' currentTs contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) =
let preferences = chatPrefsFromRow encodedPrefs receivedPrefs
profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge currentTs badgeRow, preferences, localAlias}
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
activeConn = Just conn
@@ -153,7 +155,7 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -164,13 +166,13 @@ getConnectionEntityKeys db cxt user@User {userId, userContactId} agentConnId = d
mu.group_member_id, mu.group_id, mu.index_in_group, 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.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at,
-- from GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
+4 -4
View File
@@ -116,7 +116,7 @@ createOrUpdateContactRequest
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
@@ -153,7 +153,7 @@ createOrUpdateContactRequest
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -172,8 +172,8 @@ createOrUpdateContactRequest
liftIO $
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId) :. ("" :: LocalAlias, preferences, currentTs, currentTs) :. badgeToRow badge badgeVerified)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, local_alias, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId) :. ("" :: LocalAlias, currentTs, currentTs) :. badgeToRow badge badgeVerified :. prefsToRow preferences)
profileId <- liftIO $ insertedRowId db
liftIO $
DB.execute
+7 -6
View File
@@ -323,7 +323,7 @@ getContactByConnReqHash db cxt user@User {userId} cReqHash1 cReqHash2 = do
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
@@ -747,12 +747,13 @@ updateContactProfile_' db userId profileId Profile {displayName, fullName, short
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?,
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, chat_peer_type = ?, updated_at = ?,
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
contact_domain = ?, contact_domain_proof = ?
WHERE user_id = ? AND contact_profile_id = ?
|]
((displayName, fullName, shortDescr, description, image, contactLink, preferences, peerType, updatedAt) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. (userId, profileId))
(prefsToRow preferences :. (displayName, fullName, shortDescr, description, image, contactLink, peerType, updatedAt) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. (userId, profileId))
-- update only member profile fields (when member doesn't have associated contact - we can reset contactLink and prefs)
updateMemberContactProfileReset_ :: DB.Connection -> UserId -> ProfileId -> Profile -> Maybe Bool -> IO ()
@@ -766,7 +767,7 @@ updateMemberContactProfileReset_' db userId profileId Profile {displayName, full
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?,
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = NULL, preferences = NULL, preferences_json = NULL, updated_at = ?,
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
contact_domain = ?, contact_domain_proof = ?
WHERE user_id = ? AND contact_profile_id = ?
@@ -861,7 +862,7 @@ contactRequestQuery =
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
@@ -983,7 +984,7 @@ getContact_ db cxt user@User {userId} contactId deleted = do
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
+32 -29
View File
@@ -217,7 +217,7 @@ import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (first, second)
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Char (toLower)
@@ -264,11 +264,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
import Database.SQLite.Simple.QQ (sql)
#endif
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact, Maybe Text, Maybe UTCTime)
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Text, Maybe Text) :. BadgeRow :. ContactDomainRow) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact, Maybe Text, Maybe UTCTime)
toMaybeGroupMember :: UTCTime -> Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, description, image, contactLink, peerType, Just localAlias, contactPreferences) :. badgeRow :. domainRow) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_)) =
Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, contactPreferences) :. badgeRow :. domainRow) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_))
toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, description, image, contactLink, peerType, Just localAlias, encodedPrefs, receivedPrefs) :. badgeRow :. domainRow) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_)) =
Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, encodedPrefs, receivedPrefs) :. badgeRow :. domainRow) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_))
toMaybeGroupMember _ _ _ = Nothing
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
@@ -404,11 +404,11 @@ createNewGroup db cxt user@User {userId} groupProfile incognitoProfile memberId
(display_name, full_name, short_descr, description, image,
group_type, group_link, public_group_id,
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
user_id, preferences, member_admission, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|]
((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup
:. (userId, groupPreferences, memberAdmission, currentTs, currentTs))
:. (userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
profileId <- insertedRowId db
DB.execute
db
@@ -487,8 +487,8 @@ createGroupInvitation db cxt user@User {userId} contact@Contact {contactId, acti
groupId <- liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
profileId <- insertedRowId db
DB.execute
db
@@ -918,11 +918,11 @@ createGroup_ db userId groupProfile prepared business useRelays relayOwnStatus p
(display_name, full_name, short_descr, description, image,
group_type, group_link, public_group_id,
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
user_id, preferences, member_admission, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|]
((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup
:. (userId, groupPreferences, memberAdmission, currentTs, currentTs))
:. (userId, memberAdmission, currentTs, currentTs) :. prefsToRow groupPreferences)
profileId <- insertedRowId db
DB.execute
db
@@ -1072,11 +1072,11 @@ getBaseGroupDetails db cxt User {userId, userContactId} _contactId_ search_ = do
getContactGroupPreferences :: DB.Connection -> User -> Contact -> IO [(GroupMemberRole, FullGroupPreferences)]
getContactGroupPreferences db User {userId} Contact {contactId} = do
map (second mergeGroupPreferences)
map (\(role, encodedPrefs, receivedPrefs) -> (role, mergeGroupPreferences $ groupPrefsFromRow encodedPrefs receivedPrefs))
<$> DB.query
db
[sql|
SELECT m.member_role, gp.preferences
SELECT m.member_role, gp.preferences, gp.preferences_json
FROM groups g
JOIN group_profiles gp USING (group_profile_id)
JOIN group_members m USING (group_id)
@@ -2088,8 +2088,8 @@ createJoiningMember
liftIO $
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId, preferences, currentTs, currentTs) :. badgeToRow badge badgeVerified)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId, currentTs, currentTs) :. badgeToRow badge badgeVerified :. prefsToRow preferences)
profileId <- liftIO $ insertedRowId db
case cReqMemberId_ of
Just memberId -> do
@@ -2170,8 +2170,8 @@ createBusinessRequestGroup
liftIO $
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, userId, groupPreferences, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, image, userId, currentTs, currentTs) :. prefsToRow (Just groupPreferences))
groupProfileId <- liftIO $ insertedRowId db
liftIO $
DB.execute
@@ -2441,8 +2441,8 @@ createNewMemberProfile_ db cxt User {userId} Profile {displayName, fullName, sho
badgeVerified <- verifyBadge_ (badgeKeys cxt) badge
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId, preferences, createdAt, createdAt) :. badgeToRow badge badgeVerified)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, userId, createdAt, createdAt) :. badgeToRow badge badgeVerified :. prefsToRow preferences)
profileId <- insertedRowId db
pure $ Right (ldn, profileId, badgeVerified)
@@ -2720,19 +2720,21 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
db
[sql|
UPDATE group_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
group_type = ?, group_link = ?,
group_web_page = ?, group_domain = CASE WHEN ? THEN ? ELSE group_domain END, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?,
preferences = ?, member_admission = ?, updated_at = ?
member_admission = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
WHERE user_id = ? AND group_id = ?
)
|]
( (newName, fullName, shortDescr, description, image, groupType_, groupLink_)
( prefsToRow groupPreferences
:. (newName, fullName, shortDescr, description, image, groupType_, groupLink_)
:. (groupWebPage_, isJust publicGroup, groupDomain_, domainWebPage_, allowEmbedding_, groupDomainProof_)
:. (groupPreferences, memberAdmission, currentTs, userId, groupId)
:. (memberAdmission, currentTs, userId, groupId)
)
updateGroup_ ldn currentTs = do
DB.execute
@@ -2778,14 +2780,14 @@ updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p}
db
[sql|
UPDATE group_profiles
SET preferences = ?, updated_at = ?
SET preferences = ?, preferences_json = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
WHERE user_id = ? AND group_id = ?
)
|]
(ps, currentTs, userId, groupId)
(prefsToRow (Just ps) :. (currentTs, userId, groupId))
pure (g :: GroupInfo) {groupProfile = p {groupPreferences = Just ps}, fullGroupPreferences = mergeGroupPreferences $ Just ps}
updateGroupProfileFromMember :: DB.Connection -> User -> GroupInfo -> Profile -> ExceptT StoreError IO GroupInfo
@@ -2805,15 +2807,16 @@ getGroupProfileById db groupId =
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
gp.preferences, gp.member_admission
gp.preferences, gp.preferences_json, gp.member_admission
FROM group_profiles gp
JOIN groups g ON gp.group_profile_id = g.group_profile_id
WHERE g.group_id = ?
|]
(Only groupId)
where
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (groupPreferences, memberAdmission)) =
let publicGroupAccess = toPublicGroupAccess accessRow
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (encodedPrefs, receivedPrefs, memberAdmission)) =
let groupPreferences = groupPrefsFromRow encodedPrefs receivedPrefs
publicGroupAccess = toPublicGroupAccess accessRow
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, groupPreferences, memberAdmission}
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> StoreCxt -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
+5 -5
View File
@@ -725,7 +725,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
-- GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
@@ -1148,7 +1148,7 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = do
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -3110,7 +3110,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
-- GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -3119,14 +3119,14 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
-- quoted GroupMember
rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
rp.display_name, rp.full_name, rp.short_descr, rp.description, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rp.display_name, rp.full_name, rp.short_descr, rp.description, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, rp.preferences_json,
rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, rm.member_security_code, rm.member_security_code_verified_at,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.description, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.description, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, dbp.preferences_json,
dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link, dbm.member_security_code, dbm.member_security_code_verified_at
@@ -52,6 +52,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260828_file_expiry
import Simplex.Chat.Store.Postgres.Migrations.M20260904_file_badges
import Simplex.Chat.Store.Postgres.Migrations.M20260915_user_badges
import Simplex.Chat.Store.Postgres.Migrations.M20260918_badge_issue_errors
import Simplex.Chat.Store.Postgres.Migrations.M20260923_preferences_json
import Simplex.Chat.Store.Postgres.Migrations.M20260924_simplex_name_resolved
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
@@ -105,6 +106,7 @@ schemaMigrations =
("20260904_file_badges", m20260904_file_badges, Just down_m20260904_file_badges),
("20260915_user_badges", m20260915_user_badges, Just down_m20260915_user_badges),
("20260918_badge_issue_errors", m20260918_badge_issue_errors, Just down_m20260918_badge_issue_errors),
("20260923_preferences_json", m20260923_preferences_json, Just down_m20260923_preferences_json),
("20260924_simplex_name_resolved", m20260924_simplex_name_resolved, Just down_m20260924_simplex_name_resolved)
]
@@ -0,0 +1,21 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.Postgres.Migrations.M20260923_preferences_json where
import Data.Text (Text)
import Text.RawString.QQ (r)
m20260923_preferences_json :: Text
m20260923_preferences_json =
[r|
ALTER TABLE contact_profiles ADD COLUMN preferences_json TEXT;
ALTER TABLE group_profiles ADD COLUMN preferences_json TEXT;
|]
down_m20260923_preferences_json :: Text
down_m20260923_preferences_json =
[r|
ALTER TABLE group_profiles DROP COLUMN preferences_json;
ALTER TABLE contact_profiles DROP COLUMN preferences_json;
|]
@@ -681,6 +681,7 @@ CREATE TABLE test_chat_schema.contact_profiles (
contact_domain_proof text,
contact_domain_verified smallint,
description text,
preferences_json text,
contact_domain_resolved_at timestamp with time zone,
contact_domain_expires_at timestamp with time zone
);
@@ -1044,7 +1045,8 @@ CREATE TABLE test_chat_schema.group_profiles (
group_domain text,
domain_web_page bigint,
allow_embedding bigint,
group_domain_proof text
group_domain_proof text,
preferences_json text
);
+6 -5
View File
@@ -375,10 +375,11 @@ updateUserProfileFields_' db userId profileId Profile {displayName, fullName, sh
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, chat_peer_type = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
|]
((displayName, fullName, shortDescr, description, image, contactLink, preferences, peerType, updatedAt) :. (userId, profileId))
(prefsToRow preferences :. (displayName, fullName, shortDescr, description, image, contactLink, peerType, updatedAt) :. (userId, profileId))
-- store the user's own badge credential; touches only the badge columns.
-- bumps user_member_profile_updated_at so groups receive the updated profile (with the badge) on the next message.
@@ -429,14 +430,14 @@ getUserContactProfiles db User {userId} =
<$> DB.query
db
[sql|
SELECT display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, contact_domain, preferences
SELECT display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, contact_domain, preferences, preferences_json
FROM contact_profiles
WHERE user_id = ?
|]
(Only userId)
where
toContactProfile :: (ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe SimplexDomain, Maybe Preferences) -> Profile
toContactProfile (displayName, fullName, shortDescr, description, image, contactLink, peerType, domain_, preferences) = Profile {displayName, fullName, shortDescr, description, image, contactLink, contactDomain = mkDomainClaim <$> domain_, peerType, preferences, badge = Nothing}
toContactProfile :: (ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe SimplexDomain, Maybe Text, Maybe Text) -> Profile
toContactProfile (displayName, fullName, shortDescr, description, image, contactLink, peerType, domain_, encodedPrefs, receivedPrefs) = Profile {displayName, fullName, shortDescr, description, image, contactLink, contactDomain = mkDomainClaim <$> domain_, peerType, preferences = chatPrefsFromRow encodedPrefs receivedPrefs, badge = Nothing}
createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> C.PrivateKeyEd25519 -> ExceptT StoreError IO ()
createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode linkPrivSigKey =
@@ -175,6 +175,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260828_file_expiry
import Simplex.Chat.Store.SQLite.Migrations.M20260904_file_badges
import Simplex.Chat.Store.SQLite.Migrations.M20260915_user_badges
import Simplex.Chat.Store.SQLite.Migrations.M20260918_badge_issue_errors
import Simplex.Chat.Store.SQLite.Migrations.M20260923_preferences_json
import Simplex.Chat.Store.SQLite.Migrations.M20260924_simplex_name_resolved
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
@@ -351,6 +352,7 @@ schemaMigrations =
("20260904_file_badges", m20260904_file_badges, Just down_m20260904_file_badges),
("20260915_user_badges", m20260915_user_badges, Just down_m20260915_user_badges),
("20260918_badge_issue_errors", m20260918_badge_issue_errors, Just down_m20260918_badge_issue_errors),
("20260923_preferences_json", m20260923_preferences_json, Just down_m20260923_preferences_json),
("20260924_simplex_name_resolved", m20260924_simplex_name_resolved, Just down_m20260924_simplex_name_resolved)
]
@@ -0,0 +1,20 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.SQLite.Migrations.M20260923_preferences_json where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20260923_preferences_json :: Query
m20260923_preferences_json =
[sql|
ALTER TABLE contact_profiles ADD COLUMN preferences_json TEXT;
ALTER TABLE group_profiles ADD COLUMN preferences_json TEXT;
|]
down_m20260923_preferences_json :: Query
down_m20260923_preferences_json =
[sql|
ALTER TABLE group_profiles DROP COLUMN preferences_json;
ALTER TABLE contact_profiles DROP COLUMN preferences_json;
|]
@@ -704,9 +704,11 @@ SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_co
Query:
INSERT INTO ratchets
(conn_id, ratchet_state, x3dh_pub_key_1, x3dh_pub_key_2, pq_pub_kem) VALUES (?, ?, ?, ?, ?)
(conn_id, ratchet_state, rc_verify_code_ad, rc_verify_code_pq, x3dh_pub_key_1, x3dh_pub_key_2, pq_pub_kem) VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (conn_id) DO UPDATE SET
ratchet_state = EXCLUDED.ratchet_state,
rc_verify_code_ad = EXCLUDED.rc_verify_code_ad,
rc_verify_code_pq = EXCLUDED.rc_verify_code_pq,
x3dh_priv_key_1 = NULL,
x3dh_priv_key_2 = NULL,
x3dh_pub_key_1 = EXCLUDED.x3dh_pub_key_1,
@@ -717,10 +719,12 @@ Query:
Plan:
Query:
INSERT INTO ratchets (conn_id, ratchet_state)
VALUES (?, ?)
INSERT INTO ratchets (conn_id, ratchet_state, rc_verify_code_ad, rc_verify_code_pq)
VALUES (?, ?, ?, ?)
ON CONFLICT (conn_id) DO UPDATE SET
ratchet_state = ?,
ratchet_state = EXCLUDED.ratchet_state,
rc_verify_code_ad = EXCLUDED.rc_verify_code_ad,
rc_verify_code_pq = EXCLUDED.rc_verify_code_pq,
x3dh_priv_key_1 = NULL,
x3dh_priv_key_2 = NULL,
x3dh_pub_key_1 = NULL,
@@ -1361,6 +1365,10 @@ Query: SELECT conn_id FROM connections WHERE user_id = ?
Plan:
SEARCH connections USING COVERING INDEX idx_connections_user (user_id=?)
Query: SELECT conn_id, rc_verify_code_ad, rc_verify_code_pq, CASE WHEN rc_verify_code_ad IS NULL THEN ratchet_state END FROM ratchets WHERE conn_id = ?
Plan:
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
Query: SELECT count(1) FROM connections
Plan:
SCAN connections USING COVERING INDEX idx_connections_deleted
@@ -124,7 +124,7 @@ Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified,
@@ -149,7 +149,7 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -160,13 +160,13 @@ Query:
mu.group_member_id, mu.group_id, mu.index_in_group, 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.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at,
-- from GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
@@ -405,7 +405,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -528,10 +528,11 @@ SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
group_type = ?, group_link = ?,
group_web_page = ?, group_domain = CASE WHEN ? THEN ? ELSE group_domain END, domain_web_page = ?, allow_embedding = ?, group_domain_proof = ?,
preferences = ?, member_admission = ?, updated_at = ?
member_admission = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
@@ -733,7 +734,7 @@ Plan:
Query:
SELECT
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, 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.request_shared_msg_id, c.contact_request_id, cr2.rejection_supported,
p.preferences, p.preferences_json, 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, cr2.rejection_supported,
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
@@ -1043,7 +1044,7 @@ Query:
-- GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
@@ -1317,8 +1318,8 @@ Query:
(display_name, full_name, short_descr, description, image,
group_type, group_link, public_group_id,
group_web_page, group_domain, domain_web_page, allow_embedding, group_domain_proof,
user_id, preferences, member_admission, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
user_id, member_admission, created_at, updated_at, preferences, preferences_json)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Plan:
@@ -1395,7 +1396,7 @@ Query:
-- GroupMember
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -1404,14 +1405,14 @@ Query:
-- quoted GroupMember
rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
rp.display_name, rp.full_name, rp.short_descr, rp.description, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rp.display_name, rp.full_name, rp.short_descr, rp.description, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, rp.preferences_json,
rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, rm.member_security_code, rm.member_security_code_verified_at,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.description, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.description, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, dbp.preferences_json,
dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link, dbm.member_security_code, dbm.member_security_code_verified_at
@@ -1463,7 +1464,7 @@ Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
@@ -2055,7 +2056,7 @@ Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, 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.request_shared_msg_id, ct.contact_request_id, cr2.rejection_supported,
cp.preferences, cp.preferences_json, 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, cr2.rejection_supported,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx,
@@ -2144,7 +2145,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -2174,7 +2175,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -2204,7 +2205,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified
@@ -3755,7 +3756,7 @@ Plan:
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, -- , ct.user_preferences
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, cp.preferences_json, -- , ct.user_preferences
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified
FROM contact_profiles cp
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
@@ -3818,7 +3819,7 @@ SEARCH f USING PRIMARY KEY (file_id=?)
SEARCH d USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, contact_domain, preferences
SELECT display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, contact_domain, preferences, preferences_json
FROM contact_profiles
WHERE user_id = ?
@@ -3939,7 +3940,7 @@ Query:
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
gp.preferences, gp.member_admission
gp.preferences, gp.preferences_json, gp.member_admission
FROM group_profiles gp
JOIN groups g ON gp.group_profile_id = g.group_profile_id
WHERE g.group_id = ?
@@ -5330,25 +5331,7 @@ SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?,
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
contact_domain = ?, contact_domain_proof = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?,
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = NULL, preferences = NULL, preferences_json = NULL, updated_at = ?,
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
contact_domain = ?, contact_domain_proof = ?
WHERE user_id = ? AND contact_profile_id = ?
@@ -5374,6 +5357,26 @@ Query:
Plan:
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, chat_peer_type = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET preferences = ?, preferences_json = ?,
display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, contact_link = ?, chat_peer_type = ?, updated_at = ?,
badge_proof = ?, badge_pres_header = ?, badge_expiry = ?, badge_type = ?, badge_verified = ?, badge_extra = ?, badge_master_key = ?, badge_signature = ?, badge_key_idx = ?,
contact_domain = ?, contact_domain_proof = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles SET contact_domain_verified = ?, contact_domain_resolved_at = ?, contact_domain_expires_at = ?
WHERE contact_profile_id IN (SELECT contact_profile_id FROM contacts WHERE user_id = ? AND contact_id = ?)
@@ -5486,7 +5489,7 @@ SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_profiles
SET preferences = ?, updated_at = ?
SET preferences = ?, preferences_json = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
@@ -5720,7 +5723,7 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -5730,7 +5733,7 @@ Query:
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
@@ -5758,7 +5761,7 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -5768,7 +5771,7 @@ Query:
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
@@ -5789,7 +5792,7 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -5799,7 +5802,7 @@ Query:
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
@@ -5820,7 +5823,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
@@ -5837,7 +5840,7 @@ Query:
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id, cr.rejection_supported,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences, p.preferences_json,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx,
@@ -5852,7 +5855,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5880,7 +5883,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5901,7 +5904,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5921,7 +5924,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5941,7 +5944,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5961,7 +5964,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -5981,7 +5984,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6001,7 +6004,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6021,7 +6024,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6041,7 +6044,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6061,7 +6064,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6081,7 +6084,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6101,7 +6104,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6121,7 +6124,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -6141,7 +6144,7 @@ SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JO
Query:
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -7143,15 +7146,15 @@ Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, descr
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, user_id, local_alias, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, user_id, local_alias, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
@@ -7190,10 +7193,10 @@ Plan:
Query: INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, file_type, shared_msg_id, roster_transfer_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, member_admission, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, created_at, updated_at, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_snd_item_statuses (chat_item_id, group_member_id, group_snd_item_status) VALUES (?,?,?)
@@ -7763,6 +7766,10 @@ Query: SELECT user_id FROM users WHERE local_display_name = ?
Plan:
SEARCH users USING COVERING INDEX sqlite_autoindex_users_2 (local_display_name=?)
Query: SELECT via_contact_uri FROM connections WHERE connection_id = ?
Plan:
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
Query: SELECT via_contact_uri, via_contact_uri_hash FROM connections WHERE connection_id = ?
Plan:
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
@@ -33,6 +33,7 @@ CREATE TABLE contact_profiles(
contact_domain_proof TEXT,
contact_domain_verified INTEGER,
description TEXT,
preferences_json TEXT,
contact_domain_resolved_at TEXT,
contact_domain_expires_at TEXT
) STRICT;
@@ -148,7 +149,8 @@ CREATE TABLE group_profiles(
group_domain TEXT,
domain_web_page INTEGER,
allow_embedding INTEGER,
group_domain_proof TEXT
group_domain_proof TEXT,
preferences_json TEXT
) STRICT;
CREATE TABLE groups(
group_id INTEGER PRIMARY KEY, -- local group ID
+27 -17
View File
@@ -421,8 +421,8 @@ createContact_ db cxt User {userId} Profile {displayName, fullName, shortDescr,
badgeVerified <- verifyBadge_ (badgeKeys cxt) badge
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, peerType) :. (userId, localAlias, preferences, currentTs, currentTs) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, contact_link, chat_peer_type, user_id, local_alias, created_at, updated_at, badge_proof, badge_pres_header, badge_expiry, badge_type, badge_verified, badge_extra, badge_master_key, badge_signature, badge_key_idx, contact_domain, contact_domain_proof, preferences, preferences_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
((displayName, fullName, shortDescr, description, image, contactLink, peerType) :. (userId, localAlias, currentTs, currentTs) :. badgeToRow badge badgeVerified :. contactDomainToRow contactDomain :. prefsToRow preferences)
profileId <- insertedRowId db
DB.execute
db
@@ -489,15 +489,16 @@ type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Ma
type GroupDirectInvitationRow = (Maybe ConnReqInvitation, Maybe GroupId, Maybe GroupMemberId, Maybe Int64, BoolInt)
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe BoolInt, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64) :. BadgeRow :. ContactDomainRow
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe BoolInt, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64) :. BadgeRow :. ContactDomainRow
type ContactRow = Only ContactId :. ContactRow'
type ContactDomainRow = (Maybe SimplexDomain, Maybe SimplexDomainProof, Maybe BoolInt)
toContact :: UTCTime -> StoreCxt -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) :. connRow) =
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, preferences, localAlias}
toContact now cxt user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, rejectionSupported_, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL) :. badgeRow :. domainRow) :. connRow) =
let preferences = chatPrefsFromRow encodedPrefs receivedPrefs
profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, preferences, localAlias}
activeConn = toMaybeConnection cxt connRow
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
incognito = maybe False connIncognito activeConn
@@ -539,18 +540,18 @@ getProfileById db userId profileId = do
DB.query
db
[sql|
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, -- , ct.user_preferences
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.description, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, cp.preferences_json, -- , ct.user_preferences
cp.badge_proof, cp.badge_pres_header, cp.badge_expiry, cp.badge_type, cp.badge_verified, cp.badge_extra, cp.badge_master_key, cp.badge_signature, cp.badge_key_idx, cp.contact_domain, cp.contact_domain_proof, cp.contact_domain_verified
FROM contact_profiles cp
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
|]
(userId, profileId)
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64, BoolInt) :. (Int64, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat) :. BadgeRow :. ContactDomainRow
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64, BoolInt) :. (Int64, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Text, Maybe Text, UTCTime, UTCTime, VersionChat, VersionChat) :. BadgeRow :. ContactDomainRow
toContactRequest :: UTCTime -> ContactRequestRow -> UserContactRequest
toContactRequest now ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, BI rejectionSupported) :. (profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. domainRow) = do
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, preferences, localBadge = rowToBadge now badgeRow, localAlias}
toContactRequest now ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, BI rejectionSupported) :. (profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, encodedPrefs, receivedPrefs, createdAt, updatedAt, minVer, maxVer) :. badgeRow :. domainRow) = do
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, preferences = chatPrefsFromRow encodedPrefs receivedPrefs, localBadge = rowToBadge now badgeRow, localAlias}
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt, rejectionSupported}
@@ -587,6 +588,14 @@ getConnReqInv db connId =
"SELECT conn_req_inv FROM connections WHERE connection_id = ?"
(Only connId)
getConnReqContact :: DB.Connection -> Int64 -> ExceptT StoreError IO ConnReqContact
getConnReqContact db connId =
ExceptT . firstRow fromOnly (SEConnectionNotFoundById connId) $
DB.query
db
"SELECT via_contact_uri FROM connections WHERE connection_id = ?"
(Only connId)
-- | Saves unique local display name based on passed displayName, suffixed with _N if required.
-- This function should be called inside transaction.
withLocalDisplayName :: forall a. DB.Connection -> UserId -> Text -> (Text -> IO (Either StoreError a)) -> IO (Either StoreError a)
@@ -686,18 +695,19 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519)
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact, Maybe BoolInt) :. GroupKeysRow :. GroupMemberRow
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Text, Maybe Text, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe VersionRoster, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact, Maybe BoolInt) :. GroupKeysRow :. GroupMemberRow
type PublicGroupAccessRow = (Maybe Text, Maybe SimplexDomain, Maybe BoolInt, Maybe BoolInt, Maybe SimplexDomainProof)
type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact, Maybe Text, Maybe UTCTime)
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Text, Maybe Text) :. BadgeRow :. ContactDomainRow
toGroupInfo :: UTCTime -> StoreCxt -> Int64 -> [ChatTagId] -> GroupInfoRow -> (GroupInfo, GroupKeysRow)
toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri, groupDomainVerified) :. groupKeysRow :. userMemberRow) =
toGroupInfo now cxt userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, encodedPrefs, receivedPrefs, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, rosterVersion, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri, groupDomainVerified) :. groupKeysRow :. userMemberRow) =
let membership = (toGroupMember now userContactId userMemberRow) {memberChatVRange = vr cxt}
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
groupPreferences = groupPrefsFromRow encodedPrefs receivedPrefs
fullGroupPreferences = mergeGroupPreferences groupPreferences
publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ (toPublicGroupAccess accessRow)
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission}
@@ -793,7 +803,7 @@ groupMemberQuery =
[sql|
SELECT
m.group_member_id, m.group_id, m.index_in_group, 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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
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.short_descr, p.description, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.preferences_json,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
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, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
@@ -811,8 +821,8 @@ toContactMember now cxt User {userContactId} (memberRow :. connRow) =
(toGroupMember now userContactId memberRow) {activeConn = toMaybeConnection cxt connRow}
rowToLocalProfile :: UTCTime -> ProfileRow -> LocalProfile
rowToLocalProfile now ((profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, preferences) :. badgeRow :. domainRow) =
LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, localAlias, preferences}
rowToLocalProfile now ((profileId, displayName, fullName, shortDescr, description, image, contactLink, peerType, localAlias, encodedPrefs, receivedPrefs) :. badgeRow :. domainRow) =
LocalProfile {profileId, displayName, fullName, shortDescr, description, image, contactLink, contactDomain = rowToContactDomain domainRow, contactDomainVerified = rowToDomainVerified domainRow, peerType, localBadge = rowToBadge now badgeRow, localAlias, preferences = chatPrefsFromRow encodedPrefs receivedPrefs}
toBusinessChatInfo :: Maybe SimplexDomainClaim -> BusinessChatInfoRow -> Maybe BusinessChatInfo
toBusinessChatInfo businessDomain (Just chatType, Just businessId, Just customerId) = Just BusinessChatInfo {chatType, businessId, customerId, businessDomain}
@@ -828,7 +838,7 @@ groupInfoQueryFields =
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id,
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.preferences_json, 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_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
@@ -838,7 +848,7 @@ groupInfoQueryFields =
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.description, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.preferences_json,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
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, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
+1 -1
View File
@@ -154,7 +154,7 @@ sendUpdatedLiveMessage cc sentMsg LiveMessage {chatName, chatItemId} live = do
runTerminalInput :: ChatTerminal -> ChatController -> IO ()
runTerminalInput ct cc = withChatTerm ct $ do
updateInput ct
withTermLock ct $ updateInput ct
receiveFromTTY cc ct
receiveFromTTY :: forall m. MonadTerminal m => ChatController -> ChatTerminal -> m ()
+72 -13
View File
@@ -24,8 +24,10 @@
module Simplex.Chat.Types.Preferences where
import Control.Applicative ((<|>))
import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Aeson (FromJSON (..), Object, ToJSON (..), Value (..), decodeStrictText)
import qualified Data.Aeson.Encoding as JE
import qualified Data.Aeson.TH as J
import qualified Data.Aeson.Types as JT
import qualified Data.Attoparsec.ByteString.Char8 as A
import qualified Data.ByteString.Char8 as B
import Data.Maybe (fromMaybe, isJust)
@@ -37,7 +39,7 @@ import Simplex.Chat.Types.Shared
import Simplex.Messaging.Agent.Store.DB (blobFieldDecoder, fromTextField_)
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, sumTypeJSON, taggedObjectJSON)
import Simplex.Messaging.Util (decodeJSON, encodeJSON, safeDecodeUtf8, (<$?>))
import Simplex.Messaging.Util (encodeJSON, safeDecodeUtf8, (<$?>))
data ChatFeature
= CFTimedMessages
@@ -149,6 +151,34 @@ setPreference_ f pref_ prefs =
SCFCalls -> prefs {calls = pref_}
SCFSessions -> prefs {sessions = pref_}
newtype PrefsJSON = PrefsJSON {unPrefsJSON :: Maybe Object}
deriving (Eq, Show)
instance ToJSON PrefsJSON where
toJSON _ = Null
toEncoding _ = JE.null_
omitField _ = True
instance FromJSON PrefsJSON where
parseJSON _ = pure $ PrefsJSON Nothing
omittedField = Just $ PrefsJSON Nothing
keepPrefsJSON :: (ToJSON p, HasField "_json" p PrefsJSON) => Value -> p -> p
keepPrefsJSON v ps = setField @"_json" ps . PrefsJSON $ case v of
Object o | v /= toJSON ps -> Just o
_ -> Nothing
decodePrefs :: (Value -> JT.Parser p) -> Text -> Maybe p
decodePrefs prefsP t = JT.parseMaybe prefsP =<< decodeStrictText t
prefsFromRow_ :: (Value -> JT.Parser p) -> Maybe Text -> Maybe Text -> Maybe p
prefsFromRow_ prefsP encodedPrefs receivedPrefs = (decode =<< receivedPrefs) <|> (decode =<< encodedPrefs)
where
decode = decodePrefs prefsP
prefsToRow :: HasField "_json" p PrefsJSON => Maybe p -> (Maybe p, Maybe Text)
prefsToRow ps = (ps, encodeJSON . Object <$> (unPrefsJSON . getField @"_json" =<< ps))
-- collection of optional chat preferences for the user and the contact
data Preferences = Preferences
{ timedMessages :: Maybe TimedMessagesPreference,
@@ -158,7 +188,8 @@ data Preferences = Preferences
files :: Maybe FilesPreference,
calls :: Maybe CallsPreference,
sessions :: Maybe SessionsPreference,
commands :: Maybe [ChatBotCommand]
commands :: Maybe [ChatBotCommand],
_json :: PrefsJSON
}
deriving (Eq, Show)
@@ -166,6 +197,9 @@ class HasCommands p where commands_ :: p -> Maybe [ChatBotCommand]
instance HasCommands Preferences where commands_ Preferences {commands} = commands
instance HasField "_json" Preferences PrefsJSON where
hasField p@Preferences {_json} = (\j -> p {_json = j}, _json)
data GroupFeature
= GFTimedMessages
| GFDirectMessages
@@ -371,12 +405,16 @@ data GroupPreferences = GroupPreferences
sessions :: Maybe SessionsGroupPreference,
comments :: Maybe CommentsGroupPreference,
signMessages :: Maybe SignMessagesGroupPreference,
commands :: Maybe [ChatBotCommand]
commands :: Maybe [ChatBotCommand],
_json :: PrefsJSON
}
deriving (Eq, Show)
instance HasCommands GroupPreferences where commands_ GroupPreferences {commands} = commands
instance HasField "_json" GroupPreferences PrefsJSON where
hasField p@GroupPreferences {_json} = (\j -> p {_json = j}, _json)
data ChatBotCommand
= CBCCommand
{ keyword :: Text, -- "order"
@@ -506,7 +544,8 @@ toChatPrefs FullPreferences {timedMessages, fullDelete, reactions, voice, files,
files = Just files,
calls = Just calls,
sessions = Just sessions,
commands = Just cmds
commands = Just cmds,
_json = PrefsJSON Nothing
}
defaultChatPrefs :: FullPreferences
@@ -523,7 +562,7 @@ defaultChatPrefs =
}
emptyChatPrefs :: Preferences
emptyChatPrefs = Preferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
emptyChatPrefs = Preferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing (PrefsJSON Nothing)
defaultGroupPrefs :: FullGroupPreferences
defaultGroupPrefs =
@@ -545,7 +584,7 @@ defaultGroupPrefs =
}
emptyGroupPrefs :: GroupPreferences
emptyGroupPrefs = GroupPreferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
emptyGroupPrefs = GroupPreferences Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing (PrefsJSON Nothing)
businessGroupPrefs :: Preferences -> GroupPreferences
businessGroupPrefs Preferences {timedMessages, fullDelete, reactions, voice, files, sessions, commands} =
@@ -580,7 +619,8 @@ defaultBusinessGroupPrefs =
sessions = Just $ SessionsGroupPreference FEOn Nothing,
comments = Just $ CommentsGroupPreference FEOff Nothing,
signMessages = Just $ SignMessagesGroupPreference FEOff,
commands = Nothing
commands = Nothing,
_json = PrefsJSON Nothing
}
data TimedMessagesPreference = TimedMessagesPreference
@@ -1092,7 +1132,8 @@ toGroupPreferences groupPreferences@FullGroupPreferences {commands = ListDef cmd
sessions = pref SGFSessions,
comments = pref SGFComments,
signMessages = pref SGFSignMessages,
commands = Just cmds
commands = Just cmds,
_json = PrefsJSON Nothing
}
where
pref :: SGroupFeature f -> Maybe (GroupFeaturePreference f)
@@ -1192,13 +1233,22 @@ instance FromJSON SessionsPreference where
$(J.deriveJSON (taggedObjectJSON $ dropPrefix "CBC") ''ChatBotCommand)
$(J.deriveJSON defaultJSON ''Preferences)
$(J.deriveToJSON defaultJSON ''Preferences)
chatPrefsP :: Value -> JT.Parser Preferences
chatPrefsP = $(J.mkParseJSON defaultJSON ''Preferences)
instance FromJSON Preferences where
parseJSON v = keepPrefsJSON v <$> chatPrefsP v
chatPrefsFromRow :: Maybe Text -> Maybe Text -> Maybe Preferences
chatPrefsFromRow = prefsFromRow_ chatPrefsP
instance ToField Preferences where
toField = toField . encodeJSON
instance FromField Preferences where
fromField = fromTextField_ decodeJSON
fromField = fromTextField_ $ decodePrefs chatPrefsP
$(J.deriveJSON defaultJSON ''GroupPreference)
@@ -1240,13 +1290,22 @@ instance FromJSON CommentsGroupPreference where
parseJSON v = $(J.mkParseJSON defaultJSON ''CommentsGroupPreference) v
omittedField = Just CommentsGroupPreference {enable = FEOff, duration = Nothing}
$(J.deriveJSON defaultJSON ''GroupPreferences)
$(J.deriveToJSON defaultJSON ''GroupPreferences)
groupPrefsP :: Value -> JT.Parser GroupPreferences
groupPrefsP = $(J.mkParseJSON defaultJSON ''GroupPreferences)
instance FromJSON GroupPreferences where
parseJSON v = keepPrefsJSON v <$> groupPrefsP v
groupPrefsFromRow :: Maybe Text -> Maybe Text -> Maybe GroupPreferences
groupPrefsFromRow = prefsFromRow_ groupPrefsP
instance ToField GroupPreferences where
toField = toField . encodeJSON
instance FromField GroupPreferences where
fromField = fromTextField_ decodeJSON
fromField = fromTextField_ $ decodePrefs groupPrefsP
$(J.deriveJSON defaultJSON ''FullPreferences)
+41 -43
View File
@@ -40,7 +40,7 @@ import Simplex.Chat.Options.DB
import Simplex.Chat.Store
import Simplex.Chat.Store.Profiles
import Simplex.Chat.Terminal
import Simplex.Chat.Terminal.Output (newChatTerminal)
import Simplex.Chat.Terminal.Output (WithTerminal (..), newChatTerminal)
import Simplex.Chat.Types
import Simplex.Chat.Types.Shared (GroupMemberRole (..))
import Simplex.FileTransfer.Description (kb, mb)
@@ -70,7 +70,7 @@ import Simplex.Messaging.Version.Internal
import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive)
import System.FilePath ((</>))
import qualified System.Terminal as C
import System.Terminal.Internal (VirtualTerminal (..), VirtualTerminalSettings (..), withVirtualTerminal)
import System.Terminal.Internal (Command (..), Terminal (..), VirtualTerminal (..), VirtualTerminalSettings (..), withVirtualTerminal)
import System.Timeout (timeout)
import Test.Hspec (Expectation, HasCallStack, shouldReturn)
#if defined(dbPostgres)
@@ -197,13 +197,32 @@ termSettings =
data TestCC = TestCC
{ chatController :: ChatController,
virtualTerminal :: VirtualTerminal,
chatAsync :: Async (),
termAsync :: Async (),
termQ :: TQueue String,
printOutput :: Bool
}
data TestTerminal = TestTerminal VirtualTerminal (TQueue String)
instance Terminal TestTerminal where
termType (TestTerminal t _) = termType t
termEvent (TestTerminal t _) = termEvent t
termInterrupt (TestTerminal t _) = termInterrupt t
termCommand (TestTerminal t q) c = do
case c of
PutLn -> atomically $ do
C.Position {row} <- readTVar $ virtualCursor t
rows <- readTVar $ virtualWindow t
writeTQueue q $ dropWhileEnd (== ' ') $ rows !! row
_ -> pure ()
termCommand t c
termFlush (TestTerminal t _) = termFlush t
termGetWindowSize (TestTerminal t _) = termGetWindowSize t
termGetCursorPosition (TestTerminal t _) = termGetCursorPosition t
instance WithTerminal TestTerminal where
withTerm t = ($ t)
aCfg :: AgentConfig
aCfg = (agentConfig defaultChatConfig) {tbqSize = 16}
@@ -306,29 +325,31 @@ insertUser st = withTransaction st (`DB.execute_` "INSERT INTO users (user_id) V
startTestChat_ :: TestParams -> ChatDatabase -> ChatConfig -> ChatOpts -> String -> User -> IO TestCC
startTestChat_ TestParams {tmpPath, printOutput} db cfg opts@ChatOpts {coreOptions = CoreChatOpts {maintenance}} dbPrefix user = do
t <- withVirtualTerminal termSettings pure
termQ <- newTQueueIO
t <- withVirtualTerminal termSettings $ pure . (`TestTerminal` termQ)
ct <- newChatTerminal t opts
Right cc <- newChatController db (Just user) cfg opts False
void $ execChatCommand' (SetTempFolder (tmpPath </> dbPrefix)) 0 `runReaderT` cc
chatAsync <- async $ runSimplexChat cfg opts user cc $ \_u cc' -> runChatTerminal ct cc' opts
unless maintenance $ atomically $ readTVar (agentAsync cc) >>= \a -> when (isNothing a) retry
termQ <- newTQueueIO
termAsync <- async $ readTerminalOutput t termQ
pure TestCC {chatController = cc, virtualTerminal = t, chatAsync, termAsync, termQ, printOutput}
pure TestCC {chatController = cc, chatAsync, termQ, printOutput}
stopTestChat :: TestParams -> TestCC -> IO ()
stopTestChat ps TestCC {chatController = cc@ChatController {smpAgent, chatStore}, chatAsync, termAsync} = do
stopChatController cc
uninterruptibleCancel termAsync
uninterruptibleCancel chatAsync
liftIO $ disposeAgentClient smpAgent
stopTestChat ps TestCC {chatController = cc@ChatController {smpAgent, chatStore}, chatAsync} = do
stopped <- async $ do
stopChatController cc
cancel chatAsync
disposeAgentClient smpAgent
r <- timeout 60000000 $ wait stopped
#if !defined(dbPostgres)
chatStats <- withConnection chatStore $ readTVarIO . DB.slow
atomically $ modifyTVar' (chatQueryStats ps) $ M.unionWith combineStats chatStats
agentStats <- withConnection (agentClientStore smpAgent) $ readTVarIO . DB.slow
atomically $ modifyTVar' (agentQueryStats ps) $ M.unionWith combineStats agentStats
#endif
closeDBStore chatStore
case r of
Just () -> closeDBStore chatStore
Nothing -> putStrLn "stopTestChat: chat did not stop in 60 seconds"
threadDelay 200000
#if !defined(dbPostgres)
where
@@ -381,7 +402,8 @@ withTestChatOpts :: HasCallStack => TestParams -> ChatOpts -> String -> (HasCall
withTestChatOpts ps = withTestChatCfgOpts ps testCfg
withTestChatCfgOpts :: HasCallStack => TestParams -> ChatConfig -> ChatOpts -> String -> (HasCallStack => TestCC -> IO a) -> IO a
withTestChatCfgOpts ps cfg opts dbPrefix = bracket (startTestChat ps cfg opts dbPrefix) (\cc -> cc <// 100000 >> stopTestChat ps cc)
withTestChatCfgOpts ps cfg opts dbPrefix runTest =
bracket (startTestChat ps cfg opts dbPrefix) (stopTestChat ps) (\cc -> runTest cc >>= ((cc <// 100000) $>))
-- enable output for specific test.
-- usage: withTestOutput $ testChat2 aliceProfile bobProfile $ \alice bob -> do ...
@@ -408,29 +430,6 @@ enableNamesRole TestCC {chatController = cc} = do
}
enableNames srv@UserServer {roles} = (srv :: UserServer 'PSMP) {roles = (roles :: ServerRolesOverride) {names = Just True}}
readTerminalOutput :: VirtualTerminal -> TQueue String -> IO ()
readTerminalOutput t termQ = do
let w = virtualWindow t
winVar <- atomically $ newTVar . init =<< readTVar w
forever . atomically $ do
win <- readTVar winVar
win' <- init <$> readTVar w
if win' == win
then retry
else do
let diff = getDiff win' win
forM_ diff $ writeTQueue termQ
writeTVar winVar win'
where
getDiff :: [String] -> [String] -> [String]
getDiff win win' = getDiff_ 1 (length win) win win'
getDiff_ :: Int -> Int -> [String] -> [String] -> [String]
getDiff_ n len win' win =
let diff = drop (len - n) win'
in if drop n win <> diff == win'
then map (dropWhileEnd (== ' ')) diff
else getDiff_ (n + 1) len win' win
withTmpFiles :: IO () -> IO ()
withTmpFiles =
bracket_
@@ -439,16 +438,15 @@ withTmpFiles =
testChatN :: HasCallStack => ChatConfig -> ChatOpts -> [Profile] -> (HasCallStack => [TestCC] -> IO ()) -> TestParams -> IO ()
testChatN cfg opts ps test params =
bracket (getTestCCs $ zip ps [1 ..]) endTests test
bracket (getTestCCs $ zip ps [1 ..]) (mapConcurrently_ $ stopTestChat params) $ \tcs -> do
test tcs
mapConcurrently_ (<// 100000) tcs
where
useClientServices = False
-- useClientServices = True
getTestCCs :: [(Profile, Int)] -> IO [TestCC]
getTestCCs [] = pure []
getTestCCs ((p, db) : envs') = (:) <$> createTestChat params cfg opts (show db) useClientServices p <*> getTestCCs envs'
endTests tcs = do
mapConcurrently_ (<// 100000) tcs
mapConcurrently_ (stopTestChat params) tcs
(<//) :: HasCallStack => TestCC -> Int -> Expectation
(<//) cc t = timeout t (getTermLine cc) `shouldReturn` Nothing
@@ -474,7 +472,7 @@ getTermLine' expected cc@TestCC {printOutput} =
error $ name <> ": no output for 5 seconds" <> expectedMsg
userName :: TestCC -> IO [Char]
userName (TestCC ChatController {currentUser} _ _ _ _ _) =
userName TestCC {chatController = ChatController {currentUser}} =
maybe "no current user" (\User {localDisplayName} -> T.unpack localDisplayName) <$> readTVarIO currentUser
testChat :: HasCallStack => Profile -> (HasCallStack => TestCC -> IO ()) -> TestParams -> IO ()
+15 -7
View File
@@ -21,7 +21,7 @@ import Data.Aeson (ToJSON)
import qualified Data.Aeson as J
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as LB
import Data.List (intercalate, stripPrefix)
import Data.List (intercalate, isPrefixOf, stripPrefix)
import qualified Data.Map.Strict as M
import Data.Maybe (isJust, isNothing)
import qualified Data.Text as T
@@ -283,7 +283,8 @@ testRetryConnecting ps = testChatCfgOpts2 cfg' opts' aliceProfile bobProfile tes
{ agentConfig =
testAgentCfg
{ quotaExceededTimeout = 1,
messageRetryInterval = RetryInterval2 {riFast = fastRetryInterval, riSlow = fastRetryInterval}
messageRetryInterval = RetryInterval2 {riFast = fastRetryInterval, riSlow = fastRetryInterval},
persistErrorInterval = 0
}
}
opts' =
@@ -1350,18 +1351,23 @@ testNegotiateCall =
alice ##> "/_call status @2 connected"
alice <## "ok"
threadDelay 100000
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: in progress (00:00)")])
alice #$> ("/_get chat @2 count=100", callChat, chatFeatures <> [(1, "outgoing call: in progress")])
bob ##> "/_call status @2 connected"
bob <## "ok"
threadDelay 100000
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: in progress (00:00)")])
bob #$> ("/_get chat @2 count=100", callChat, chatFeatures <> [(0, "incoming call: in progress")])
-- either party can end the call
bob ##> "/_call end @2"
bob <## "ok"
threadDelay 100000
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: ended (00:00)")])
bob #$> ("/_get chat @2 count=100", callChat, chatFeatures <> [(0, "incoming call: ended")])
alice <## "call with bob ended"
alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: ended (00:00)")])
alice #$> ("/_get chat @2 count=100", callChat, chatFeatures <> [(1, "outgoing call: ended")])
where
callChat = map (fmap noDuration) . chat
noDuration s = case words s of
ws@(_ : _) | "(0" `isPrefixOf` last ws -> unwords $ init ws
_ -> s
testStopStartChat :: HasCallStack => TestParams -> IO ()
testStopStartChat ps =
@@ -3111,13 +3117,15 @@ testMsgDecryptError ps =
withTestChat ps "bob" $ \bob -> do
bob <## "subscribed 1 connections on server localhost"
alice #> "@bob hello again"
bob <# "alice> skipped message ID 9..11"
bob <# "alice> skipped message ID 7..9"
bob <# "alice> hello again"
bob #> "@alice received!"
alice <# "bob> received!"
setupDesynchronizedRatchet :: HasCallStack => TestParams -> TestCC -> IO ()
setupDesynchronizedRatchet ps alice = do
alice ##> "/set receipts all off"
alice <## "ok"
copyDb "bob" "bob_old"
withTestChat ps "bob" $ \bob -> do
bob <## "subscribed 1 connections on server localhost"
+7 -1
View File
@@ -4149,13 +4149,15 @@ testGroupMsgDecryptError ps =
withTestChat ps "bob" $ \bob -> do
bob <## "subscribed 2 connections on server localhost"
alice #> "#team hello again"
bob <# "#team alice> skipped message ID 8..10"
bob <# "#team alice> skipped message ID 6..8"
bob <# "#team alice> hello again"
bob #> "#team received!"
alice <# "#team bob> received!"
setupDesynchronizedRatchet :: HasCallStack => TestParams -> TestCC -> IO ()
setupDesynchronizedRatchet ps alice = do
alice ##> "/set receipts all off"
alice <## "ok"
copyDb "bob" "bob_old"
withTestChat ps "bob" $ \bob -> do
bob <## "subscribed 2 connections on server localhost"
@@ -4167,6 +4169,7 @@ setupDesynchronizedRatchet ps alice = do
bob <# "#team alice> 3"
bob #> "#team 4"
alice <# "#team bob> 4"
threadDelay 500000
withTestChat ps "bob_old" $ \bob -> do
bob <## "subscribed 2 connections on server localhost"
bob ##> "/sync #team alice"
@@ -5234,6 +5237,9 @@ testMemberContactAccept =
cath #$> ("/_get chat @3 count=1", chat, [(0, "requested connection from group team")])
cath ##> "/_connect contact 1 3"
cath <## "bad chat command: contact is a member contact request"
cath ##> "/accept_member_contact @bob"
cath <## "contact bob is accepted, starting connection"
concurrently_
+47 -2
View File
@@ -67,6 +67,7 @@ chatProfileTests = do
it "rotate address ratchet keys" testRotateAddressRatchetKeys
it "create address on specified server" testCreateAddressOnServer
it "retry connecting via contact link" testRetryConnectingViaContactLink
it "retry connecting via address in contact profile after address keys rotation" testRetryConnectingContactViaAddress
it "add contact link to profile" testProfileLink
it "auto accept contact requests" testUserContactLinkAutoAccept
it "deduplicate contact requests" testDeduplicateContactRequests
@@ -822,6 +823,50 @@ testRetryConnectingViaContactLink ps = testChatCfgOpts2 cfg' opts' aliceProfile
}
}
testRetryConnectingContactViaAddress :: HasCallStack => TestParams -> IO ()
testRetryConnectingContactViaAddress ps =
withNewTestChatOpts ps testOptsNoFullLinks "alice" aliceProfile $ \alice ->
withNewTestChatCfgOpts ps cfg' opts' "bob" bobProfile $ \bob -> do
alice ##> "/ad"
sLink <- getContactLink_ alice True
alice ##> "/pa on"
alice <## "new contact address set"
rotateAddressKeys alice
case A.parseOnly strP (B.pack sLink) of
Left _ -> error "error parsing contact link"
Right shortLink -> do
void $ withCCUser bob $ \user -> withCCTransaction bob $ \db -> runExceptT $ createContact db (storeCxt $ chatController bob) user aliceProfile {contactLink = Just shortLink}
bob ##> "/_connect contact 1 2"
bob <##. "smp agent error: BROKER"
rotateAddressKeys alice
withSmpServer' serverCfg' $ do
bob ##> "/_connect contact 1 2"
bob <## "connection request sent!"
alice <## "bob (Bob) wants to connect to you!"
alice <## "to accept: /ac bob"
alice <## "to reject: /rc bob (the sender will NOT be notified)"
alice ##> "/ac bob"
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected")
alice <##> bob
bob <## "disconnected 1 connections on server localhost"
where
rotateAddressKeys alice = do
alice ##> "/_rotate_address_keys 1"
_ <- getContactLink_ alice False
alice <## "auto_accept off"
serverCfg' = smpServerCfg {transports = [("7003", transport @TLS, False)]}
cfg' = testCfg {agentConfig = testAgentCfg {persistErrorInterval = 0}}
opts' =
testOpts
{ coreOptions =
testCoreOpts
{ smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7003"]
}
}
testProfileLink :: HasCallStack => TestParams -> IO ()
testProfileLink =
testChat3 aliceProfile bobProfile cathProfile $
@@ -1627,7 +1672,7 @@ testPlanAddressContactViaAddress =
bob ##> ("/c " <> cLink)
connecting alice bob
bob ##> "/delete @alice"
bob ##> "/delete @alice notify=off"
bob <## "alice: contact is deleted"
alice ##> "/delete @bob"
alice <## "bob: contact is deleted"
@@ -1689,7 +1734,7 @@ testPlanAddressContactViaShortAddress =
bob ##> ("/c " <> sLink)
connecting alice bob
bob ##> "/delete @alice"
bob ##> "/delete @alice notify=off"
bob <## "alice: contact is deleted"
alice ##> "/delete @bob"
alice <## "bob: contact is deleted"
+1 -1
View File
@@ -745,7 +745,7 @@ connectUsers_ cc1 cc2 noShortLink = do
(cc1 <## (name2 <> ": contact is connected"))
showName :: TestCC -> IO String
showName (TestCC ChatController {currentUser} _ _ _ _ _) = do
showName TestCC {chatController = ChatController {currentUser}} = do
Just User {localDisplayName, profile = LocalProfile {fullName, shortDescr}} <- readTVarIO currentUser
pure . T.unpack $ viewName localDisplayName <> optionalFullName localDisplayName fullName shortDescr
+39 -2
View File
@@ -1,5 +1,6 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
@@ -12,6 +13,7 @@ import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.List (isInfixOf)
import qualified Data.List.NonEmpty as L
import Data.Maybe (fromMaybe)
import Data.Time.Clock.System (SystemTime (..), systemToUTCTime)
import Simplex.Chat.Library.Internal (decodeLinkUserData, encodeShortLinkData)
import Simplex.Chat.Protocol
@@ -33,6 +35,41 @@ protocolTests = do
decodeChatMessageTest
shortLinkDataTests
batchLimitTests
preferencesJSONTests
preferencesJSONTests :: Spec
preferencesJSONTests = describe "preferences JSON" $ do
it "stores no JSON when preferences encode to what was received" $ do
ps <- prefs "{\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"no\"}}"
storedJSON ps `shouldBe` Nothing
it "stores no JSON when group preferences encode to what was received" $ do
ps <- groupPrefs "{\"voice\":{\"enable\":\"on\"},\"reactions\":{\"enable\":\"off\"}}"
storedJSON ps `shouldBe` Nothing
it "stores JSON with a preference that is not defined" $ do
let s = "{\"voice\":{\"enable\":\"on\"},\"polls\":{\"enable\":\"on\"}}"
ps <- groupPrefs s
storedJSON ps `shouldBe` Just (object s)
J.toJSON ps `shouldBe` object "{\"voice\":{\"enable\":\"on\"}}"
it "stores JSON with a field that is not defined in a preference" $ do
let s = "{\"voice\":{\"enable\":\"on\",\"exceptRole\":\"observer\"}}"
ps <- groupPrefs s
storedJSON ps `shouldBe` Just (object s)
it "reads the received preferences from the stored JSON" $
groupPrefsFromRow (Just "{\"voice\":{\"enable\":\"on\"}}") (Just "{\"voice\":{\"enable\":\"off\"}}")
`shouldBe` groupPrefs_ "{\"voice\":{\"enable\":\"off\"}}"
it "reads the stored preferences when the received JSON does not parse" $
groupPrefsFromRow (Just "{\"voice\":{\"enable\":\"on\"}}") (Just "{\"voice\":{\"enable\":\"sometimes\"}}")
`shouldBe` groupPrefs_ "{\"voice\":{\"enable\":\"on\"}}"
where
prefs :: ByteString -> IO Preferences
prefs = either fail pure . J.eitherDecodeStrict'
groupPrefs :: ByteString -> IO GroupPreferences
groupPrefs = either fail pure . J.eitherDecodeStrict'
groupPrefs_ :: ByteString -> Maybe GroupPreferences
groupPrefs_ = J.decodeStrict'
storedJSON ps = J.decodeStrictText =<< snd (prefsToRow $ Just ps) :: Maybe J.Value
object :: ByteString -> J.Value
object s = fromMaybe (error $ "not JSON: " <> B.unpack s) $ J.decodeStrict' s
batchLimitTests :: Spec
batchLimitTests = describe "Chat message batch limits" $ do
@@ -133,10 +170,10 @@ s #==# msg = do
s ==# msg
testChatPreferences :: Maybe Preferences
testChatPreferences = Just Preferences {voice = Just VoicePreference {allow = FAYes}, files = Nothing, fullDelete = Nothing, timedMessages = Nothing, calls = Nothing, reactions = Just ReactionsPreference {allow = FAYes}, sessions = Nothing, commands = Nothing}
testChatPreferences = Just Preferences {voice = Just VoicePreference {allow = FAYes}, files = Nothing, fullDelete = Nothing, timedMessages = Nothing, calls = Nothing, reactions = Just ReactionsPreference {allow = FAYes}, sessions = Nothing, commands = Nothing, _json = PrefsJSON Nothing}
testGroupPreferences :: Maybe GroupPreferences
testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing, support = Nothing, sessions = Nothing, comments = Nothing, signMessages = Nothing, commands = Nothing}
testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing, support = Nothing, sessions = Nothing, comments = Nothing, signMessages = Nothing, commands = Nothing, _json = PrefsJSON Nothing}
testProfile :: Profile
testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, description = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), peerType = Nothing, contactLink = Nothing, preferences = testChatPreferences, badge = Nothing, contactDomain = Nothing}
+24 -235
View File
@@ -220,61 +220,6 @@ templateEngineOverride: njk
color: #dfeaff;
}
.cf-hero-offer {
position: relative;
margin-top: calc(var(--sec-vhu) * 6.3);
width: calc(var(--sec-vwu) * 56);
}
.cf-hero-offer p { font-family: "Manrope", sans-serif; }
.cf-hero-offer .cf-offer-lead {
font-weight: 600;
font-size: calc(var(--sec-vwu) * 2.1);
line-height: 1.4;
color: #ffffff;
font-variant-numeric: tabular-nums;
}
.cf-hero-offer .cf-offer-lead b {
font-weight: 700;
background: linear-gradient(90deg, #019bfe 0%, #64fdff 58%, #c8feff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: transparent;
}
.cf-hero-offer .cf-offer-lead span { white-space: nowrap; }
.cf-hero-offer .cf-offer-terms {
font-weight: 300;
font-size: calc(var(--sec-vwu) * 1.55);
line-height: 1.55;
color: #dfeaff;
margin-top: calc(var(--sec-vhu) * 0.4);
}
.cf-hero-offer .cf-offer-terms a { position: relative; color: inherit; text-decoration: none; }
.cf-hero-offer .cf-offer-terms abbr { text-decoration: underline dotted; text-underline-offset: 3px; cursor: pointer; }
.cf-hero-offer .cf-offer-terms a::after {
content: attr(data-tip);
position: absolute;
left: calc(100% + 12px);
top: 50%;
transform: translateY(-50%);
white-space: nowrap;
padding: 7px 12px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, .18);
background: rgba(5, 9, 32, .96);
color: #dfeaff;
font-family: "Manrope", sans-serif;
font-weight: 400;
font-size: calc(var(--sec-vwu) * 1.1);
line-height: 1.4;
opacity: 0;
visibility: hidden;
transition: opacity .12s ease;
pointer-events: none;
}
.cf-hero-offer .cf-offer-terms a:hover::after,
.cf-hero-offer .cf-offer-terms a:focus-visible::after { opacity: 1; visibility: visible; }
/* ---- close: same left-aligned layout as the hero ---- */
.cf-close .area { position: relative; display: flex; align-items: flex-end; padding: 0 calc(var(--sec-vwu) * 7.5) calc(var(--sec-vhu) * 22) 0; }
@@ -655,7 +600,7 @@ templateEngineOverride: njk
--cf-phone-h: calc(var(--cf-phone-w) / 0.4914);
--cf-phone-part: 0.55;
/* bottom of the hero text block, measured from the section top */
--cf-hero-text-b: calc(var(--cf-top-base) + max(0px, calc(-1 * var(--cf-area-off))) + 92.5vw);
--cf-hero-text-b: calc(var(--cf-top-base) + max(0px, calc(-1 * var(--cf-area-off))) + 75vw);
/* the arc crest starts 0.2678 down the cover image, which is
2.349 phone widths tall and sits 0.4034 phone heights above
the phone top: the phone may rise no higher than this */
@@ -714,19 +659,6 @@ templateEngineOverride: njk
}
.cf-close-offer p { font-size: calc(var(--sec-vwu) * 3.6); }
.cf-stats { top: 0; gap: calc(var(--sec-vwu) * 8); margin-top: calc(var(--sec-vhu) * 3); }
.cf-hero .cf-hero-offer {
position: relative;
top: 0;
width: auto;
z-index: 4;
margin-top: calc(var(--sec-vhu) * 2);
padding: 10px var(--cf-text-pad) 12px;
margin-left: calc(-1 * var(--cf-gutter));
margin-right: calc(-1 * var(--cf-gutter));
background: linear-gradient(to bottom, rgba(2, 7, 29, 0) 0%, rgba(2, 7, 29, var(--cf-offer-veil, 0)) 22%, rgba(2, 7, 29, var(--cf-offer-veil, 0)) 78%, rgba(2, 7, 29, 0) 100%);
}
.cf-hero-offer .cf-offer-lead { font-size: calc(var(--sec-vwu) * 4.2); line-height: 1.3; white-space: nowrap; }
.cf-hero-offer .cf-offer-terms { font-size: calc(var(--sec-vwu) * 4); margin-top: calc(var(--sec-vhu) * 0.8); }
.cf-stat img { width: calc(var(--sec-vwu) * 8); }
.cf-stat b { font-size: calc(var(--sec-vwu) * 6.5); }
.cf-stat span { font-size: calc(var(--sec-vwu) * 3.4); }
@@ -826,11 +758,9 @@ templateEngineOverride: njk
.cf-tag .n { font-size: 3.54cqh; line-height: 1.3; }
}
/* tall phones (modern ratios, with or without the browser bars): the headline takes the
room the screen has; short ones keep the smaller size. */
@media screen and (max-width: 959px) and (orientation: portrait) and (max-aspect-ratio: 62/100) {
.cf-hero h1 { font-size: calc(var(--sec-vwu) * 9.6); }
:root { --cf-hero-text-b: calc(var(--cf-top-base) + max(0px, calc(-1 * var(--cf-area-off))) + 91vw); }
:root { --cf-hero-text-b: calc(var(--cf-top-base) + max(0px, calc(-1 * var(--cf-area-off))) + 72.8vw); }
}
/* 320pt phones: keep the headline at three lines */
@@ -1106,10 +1036,6 @@ templateEngineOverride: njk
<div><b>520K</b><span>Monthly users</span></div>
</div>
</div>
<div class="cf-hero-offer" id="cf-offer">
<p class="cf-offer-lead"><span id="cf-offer-what">SimpleX name for 7 years</span><span id="cf-offer-clock"></span></p>
<p class="cf-offer-terms" id="cf-offer-terms">Invest $500+ by the end of September 22, <a href="https://en.wikipedia.org/wiki/Anywhere_on_Earth" target="_blank" rel="noopener" data-tip="Anywhere on Earth: UTC−12, the last time zone" aria-label="Anywhere on Earth: UTC−12, the last time zone"><abbr>AoE</abbr></a>.</p>
</div>
</div>
<img class="cf-phone" src="/img/crowdfunding/phone.png" alt="">
<img class="cf-mask" src="/img/crowdfunding/phone-mask.png" alt="">
@@ -1231,13 +1157,13 @@ templateEngineOverride: njk
</div>
</div>
<div class="cf-close-offer">
<p>Invest $500+ by November 22 during early bird terms and get a SimpleX public name for your channel or business for 5 years, ahead of public launch.</p>
<p>Invest $500+ during early bird terms and get a SimpleX public name for your channel or business for 5 years, ahead of public launch.</p>
</div>
</div>
</section>
<!-- 8 · FOOTER -->
<section class="page cf cf-footer" data-section="footer">
<section class="page cf cf-footer">
<div class="cf-footer-inner">
<p class="cf-fine">This Regulation Crowdfunding offering is made available through Wefunder Portal LLC. This investment involves a high degree of risk, including the possible loss of your investment.</p>
<p class="cf-fine cf-fine-links">
@@ -1388,119 +1314,33 @@ templateEngineOverride: njk
</div>
<script>
(function cfOffer() {
var offer = document.getElementById('cf-offer');
var what = document.getElementById('cf-offer-what');
var clock = document.getElementById('cf-offer-clock');
var terms = document.getElementById('cf-offer-terms');
if (!offer || !what || !clock || !terms) return;
var PHASES = [
{ until: Date.UTC(2026, 8, 23, 12, 0, 0), what: 'SimpleX name for 7 years', terms: null },
{ until: Date.UTC(2026, 10, 23, 12, 0, 0), what: 'SimpleX name for 5 years',
terms: 'Invest $500+ during early bird terms.',
wide: 'Invest $500+ during early bird terms, and get the name ahead of public launch.' },
{ until: Infinity, what: 'SimpleX name for 5 years',
terms: 'Invest $500+ during early bird terms.' }
];
var shown = -1;
var digits = null;
var lead = what.parentNode;
var block = offer.parentNode;
(function cfHero() {
var block = document.querySelector('.cf-hero .cf-hero-text');
if (!block) return;
var hero = block.closest('section');
var leadShort = document.querySelector('.cf-hero .lead-short');
var logos = document.querySelectorAll('#navbar .logo img');
var narrow = window.matchMedia('(max-width: 959px) and (orientation: portrait)');
function logoBottom() {
var bottom = 0;
for (var i = 0; i < logos.length; i++) { var r = logos[i].getBoundingClientRect(); if (r.height > 0) bottom = Math.max(bottom, r.bottom); }
return bottom;
}
var canvas = document.createElement('canvas').getContext('2d');
function metrics(el, text) {
var cs = getComputedStyle(el);
canvas.font = cs.fontWeight + ' ' + cs.fontSize + ' ' + cs.fontFamily;
var m = canvas.measureText(text);
if (m.fontBoundingBoxAscent === undefined) return null;
return { asc: m.fontBoundingBoxAscent, desc: m.fontBoundingBoxDescent, inkUp: m.actualBoundingBoxAscent, inkDown: m.actualBoundingBoxDescent, lineHeight: parseFloat(cs.lineHeight) };
}
var probe = document.createElement('span');
probe.style.cssText = 'display:inline-block;width:0;height:0;vertical-align:baseline;overflow:hidden';
function baselineOf(el) {
el.insertBefore(probe, el.firstChild);
var y = probe.getBoundingClientRect().bottom;
el.removeChild(probe);
return y;
}
function blockBaseline(el) { return baselineOf(el); }
function inlineBaseline(el) { return baselineOf(el); }
var raster = document.createElement('canvas');
function inkRows(el, text, baseline) {
var cs = getComputedStyle(el);
var size = parseFloat(cs.fontSize);
var top = Math.floor(baseline - size * 1.5), height = Math.ceil(size * 2.5), width = Math.ceil(size * text.length) + 8;
if (raster.width !== width || raster.height !== height) { raster.width = width; raster.height = height; }
var g = raster.getContext('2d');
g.clearRect(0, 0, width, height);
g.font = cs.fontWeight + ' ' + cs.fontSize + ' ' + cs.fontFamily;
g.fillStyle = '#fff';
g.textBaseline = 'alphabetic';
g.fillText(text, 4, baseline - top);
var px = g.getImageData(0, 0, width, height).data, first = -1, last = -1, y, x;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) { if (px[(y * width + x) * 4 + 3] > 150) { if (first < 0) first = y; last = y; break; } }
}
return first < 0 ? null : { top: top + first, bottom: top + last };
}
function statsInk(figures, labels) {
var top = Infinity, bottom = -Infinity, i, rows;
for (i = 0; i < figures.length; i++) {
var mf = metrics(figures[i], figures[i].textContent);
rows = mf && inkRows(figures[i], figures[i].textContent, blockBaseline(figures[i]));
if (rows) top = Math.min(top, rows.top);
}
for (i = 0; i < labels.length; i++) {
var body = labels[i].textContent.replace(/[gjpqy]/g, 'n');
var ml = metrics(labels[i], body);
rows = ml && inkRows(labels[i], body, inlineBaseline(labels[i]));
if (rows) bottom = Math.max(bottom, rows.bottom);
}
return isFinite(top) && isFinite(bottom) ? { top: top, bottom: bottom } : null;
}
var phoneImg = document.querySelector('.cf-phone');
function veil() {
offer.style.removeProperty('--cf-offer-veil');
if (!narrow.matches || !phoneImg) return;
var phone = phoneImg.getBoundingClientRect();
var arcTop = phone.top - phone.height * 0.4034 + phone.width * 2.349 * 0.2678;
var need = offer.getBoundingClientRect().bottom + 8 - arcTop;
var alpha = Math.max(0, Math.min(1, need / 40)) * 0.82;
if (alpha > 0) offer.style.setProperty('--cf-offer-veil', alpha.toFixed(2));
function fit() {
if (!leadShort) return;
leadShort.style.fontSize = '';
if (!narrow.matches) return;
var cs = getComputedStyle(leadShort);
var base = parseFloat(cs.fontSize);
var box = leadShort.clientWidth - parseFloat(cs.paddingLeft) - parseFloat(cs.paddingRight);
var range = document.createRange();
range.selectNodeContents(leadShort);
var line = range.getBoundingClientRect().width;
if (base > 0 && box > 0 && line > 0) leadShort.style.fontSize = (base * box / line).toFixed(2) + 'px';
}
function place() {
offer.style.marginTop = '';
block.style.transform = '';
veil();
if (narrow.matches) return;
var pill = block.querySelector('.cf-hero-cta .cf-pill');
var figures = block.querySelectorAll('.cf-stats .cf-stat b');
var labels = block.querySelectorAll('.cf-stats .cf-stat span');
var mo = metrics(lead, lead.textContent);
var ink = pill && figures.length && labels.length && mo ? statsInk(figures, labels) : null;
if (!ink) return;
var above = ink.top - Math.round(pill.getBoundingClientRect().bottom);
var text = lead.textContent;
var leadInkTop = blockBaseline(lead) - mo.inkUp;
var base = Math.round(parseFloat(getComputedStyle(offer).marginTop) + (ink.bottom + 1 + above) - leadInkTop);
var best = null;
for (var d = -3; d <= 3; d++) {
offer.style.marginTop = (base + d) + 'px';
var now = statsInk(figures, labels);
var lit = inkRows(lead, text, blockBaseline(lead));
if (!now || !lit) break;
var below = lit.top - now.bottom - 1;
if (best === null || Math.abs(below - above) < Math.abs(best.below - above)) best = { d: d, below: below };
if (below === above) break;
}
if (best) offer.style.marginTop = (base + best.d) + 'px';
if (!hero) return;
if (narrow.matches || !hero) return;
var unit = Math.min(window.innerWidth * 1080 / 1920, window.innerHeight) / 100;
var top = block.getBoundingClientRect().top;
var minTop = logoBottom() + unit * 2;
@@ -1508,59 +1348,9 @@ templateEngineOverride: njk
var down = Math.min(Math.max(0, minTop - top), Math.max(0, spare));
if (down > 0) block.style.transform = 'translateY(' + Math.round(down) + 'px)';
}
function two(n) { return (n < 10 ? '0' : '') + n; }
var leadShort = document.querySelector('.cf-hero .lead-short');
function fitLine(el) {
el.style.fontSize = '';
if (!narrow.matches) return;
var cs = getComputedStyle(el);
var base = parseFloat(cs.fontSize);
var box = el.clientWidth - parseFloat(cs.paddingLeft) - parseFloat(cs.paddingRight);
var range = document.createRange();
range.selectNodeContents(el);
var line = range.getBoundingClientRect().width;
if (base > 0 && box > 0 && line > 0) el.style.fontSize = (base * box / line).toFixed(2) + 'px';
}
function fit() {
fitLine(lead);
if (leadShort) fitLine(leadShort);
}
var narrow = window.matchMedia('(max-width: 959px) and (orientation: portrait)');
var desktop = window.matchMedia('(min-width: 960px)');
function left(ms) {
var total = Math.floor(ms / 1000);
var days = Math.floor(total / 86400);
var rest = total - days * 86400;
var hms = two(Math.floor(rest / 3600)) + ':' + two(Math.floor(rest / 60) % 60) + ':' + two(rest % 60);
return days > 0 ? days + (days === 1 ? ' day ' : ' days ') + hms : hms;
}
function tick() {
var now = Date.now();
var i = 0;
while (i < PHASES.length && now >= PHASES[i].until) i++;
var timed = PHASES[i].until !== Infinity;
var changed = i !== shown;
if (changed) {
shown = i;
what.textContent = PHASES[i].what + (timed ? ':' : '');
var line = desktop.matches && PHASES[i].wide ? PHASES[i].wide : PHASES[i].terms;
if (line !== null) terms.textContent = line;
if (!timed) { clock.textContent = ''; clearInterval(timer); fit(); return; }
}
if (!digits) {
digits = document.createElement('b');
clock.append(' ', digits, ' left');
changed = true;
}
var text = left(PHASES[i].until - now);
if (text.length !== digits.textContent.length) changed = true;
digits.textContent = text;
if (changed) fit();
}
var timer = setInterval(tick, 1000);
tick();
fit();
place();
window.addEventListener('resize', function () { shown = -1; tick(); fit(); place(); });
window.addEventListener('resize', function () { fit(); place(); });
if (document.fonts && document.fonts.ready) document.fonts.ready.then(function () { fit(); place(); });
})();
</script>
@@ -1582,8 +1372,7 @@ templateEngineOverride: njk
}, { threshold: 0.4 })
sections.forEach(function (s) { watch.observe(s) })
var places = [['#cf-hero-invest', 'Invest', 'hero'], ['.cf-close .cf-primary', 'Invest', 'close'], ['.cf-fine-links a[href*="wefunder"]', 'Invest', 'footer'],
['#cf-hero-subscribe', 'Subscribe', 'hero'], ['.cf-hero-corner', 'Subscribe', 'corner'], ['.cf-close .cf-cta-link', 'Subscribe', 'close'],
['#cf-offer-terms a', 'AoE', 'hero']]
['#cf-hero-subscribe', 'Subscribe', 'hero'], ['.cf-hero-corner', 'Subscribe', 'corner'], ['.cf-close .cf-cta-link', 'Subscribe', 'close']]
places.forEach(function (p) {
var el = document.querySelector(p[0])
if (el) el.addEventListener('click', function () { track(p[1], { props: { place: p[2] } }) })