mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 05:24:44 +00:00
directory: move all storage to the database, optimize performance (#6287)
* core: add custom indexed columns to groups and contacts * directory: use database (TODO search/listing) * triggers to maintain current member count * update simplexmq, fix tests, use summary from GroupInfo * fix all directory tests * remove acceptance fields from group reg * enable all tests * clean up * postgres migrations, fixes * query plans * use function in postgres triggers, improve sqlite query * fix export/import * update schema * prevent admins from promoting groups when approving * update listing every 5 minutes
This commit is contained in:
@@ -313,8 +313,8 @@ data ChatCommand
|
||||
| APIGetAppSettings (Maybe AppSettings)
|
||||
| APIGetChatTags UserId
|
||||
| APIGetChats {userId :: UserId, pendingConnections :: Bool, pagination :: PaginationByTime, query :: ChatListQuery}
|
||||
| APIGetChat {chatRef :: ChatRef, contentTag :: Maybe MsgContentTag, chatPagination :: ChatPagination, search :: Maybe String}
|
||||
| APIGetChatItems {chatPagination :: ChatPagination, search :: Maybe String}
|
||||
| APIGetChat {chatRef :: ChatRef, contentTag :: Maybe MsgContentTag, chatPagination :: ChatPagination, search :: Maybe Text}
|
||||
| APIGetChatItems {chatPagination :: ChatPagination, search :: Maybe Text}
|
||||
| APIGetChatItemInfo {chatRef :: ChatRef, chatItemId :: ChatItemId}
|
||||
| APISendMessages {sendRef :: SendRef, liveMessage :: Bool, ttl :: Maybe Int, composedMessages :: NonEmpty ComposedMessage}
|
||||
| APICreateChatTag ChatTagData
|
||||
@@ -511,8 +511,8 @@ data ChatCommand
|
||||
| ClearGroup GroupName
|
||||
| ListMembers GroupName
|
||||
| ListMemberSupportChats GroupName
|
||||
| APIListGroups {userId :: UserId, contactId_ :: Maybe ContactId, search :: Maybe String}
|
||||
| ListGroups (Maybe ContactName) (Maybe String)
|
||||
| APIListGroups {userId :: UserId, contactId_ :: Maybe ContactId, search :: Maybe Text}
|
||||
| ListGroups (Maybe ContactName) (Maybe Text)
|
||||
| UpdateGroupNames GroupName GroupProfile
|
||||
| ShowGroupProfile GroupName
|
||||
| UpdateGroupDescription GroupName (Maybe Text)
|
||||
@@ -524,7 +524,7 @@ data ChatCommand
|
||||
| SendGroupMessageQuote {groupName :: GroupName, contactName_ :: Maybe ContactName, quotedMsg :: Text, message :: Text}
|
||||
| ClearNoteFolder
|
||||
| LastChats (Maybe Int) -- UserId (not used in UI)
|
||||
| LastMessages (Maybe ChatName) Int (Maybe String) -- UserId (not used in UI)
|
||||
| LastMessages (Maybe ChatName) Int (Maybe Text) -- UserId (not used in UI)
|
||||
| LastChatItemId (Maybe ChatName) Int -- UserId (not used in UI)
|
||||
| ShowChatItem (Maybe ChatItemId) -- UserId (not used in UI)
|
||||
| ShowChatItemInfo ChatName Text
|
||||
@@ -647,7 +647,7 @@ data ChatResponse
|
||||
| CRChatItemTTL {user :: User, chatItemTTL :: Maybe Int64}
|
||||
| CRNetworkConfig {networkConfig :: NetworkConfig}
|
||||
| CRContactInfo {user :: User, contact :: Contact, connectionStats_ :: Maybe ConnectionStats, customUserProfile :: Maybe Profile}
|
||||
| CRGroupInfo {user :: User, groupInfo :: GroupInfo, groupSummary :: GroupSummary}
|
||||
| CRGroupInfo {user :: User, groupInfo :: GroupInfo}
|
||||
| CRGroupMemberInfo {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats_ :: Maybe ConnectionStats}
|
||||
| CRQueueInfo {user :: User, rcvMsgInfo :: Maybe RcvMsgInfo, queueInfo :: ServerQueueInfo}
|
||||
| CRContactSwitchStarted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
@@ -682,7 +682,7 @@ data ChatResponse
|
||||
| CRContactRequestRejected {user :: User, contactRequest :: UserContactRequest, contact_ :: Maybe Contact}
|
||||
| CRUserAcceptedGroupSent {user :: User, groupInfo :: GroupInfo, hostContact :: Maybe Contact}
|
||||
| CRUserDeletedMembers {user :: User, groupInfo :: GroupInfo, members :: [GroupMember], withMessages :: Bool}
|
||||
| CRGroupsList {user :: User, groups :: [GroupInfoSummary]}
|
||||
| CRGroupsList {user :: User, groups :: [GroupInfo]}
|
||||
| CRSentGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember}
|
||||
| CRFileTransferStatus User (FileTransfer, [Integer]) -- TODO refactor this type to FileTransferStatus
|
||||
| CRFileTransferStatusXFTP User AChatItem
|
||||
|
||||
@@ -1601,9 +1601,8 @@ processChatCommand vr nm = \case
|
||||
case activeConn of
|
||||
Just conn -> getConnQueueInfo user conn
|
||||
Nothing -> throwChatError $ CEContactNotActive ct
|
||||
APIGroupInfo gId -> withUser $ \user -> do
|
||||
(g, s) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> liftIO (getGroupSummary db user gId)
|
||||
pure $ CRGroupInfo user g s
|
||||
APIGroupInfo gId -> withUser $ \user ->
|
||||
CRGroupInfo user <$> withFastStore (\db -> getGroupInfo db vr user gId)
|
||||
APIGroupMemberInfo gId gMemberId -> withUser $ \user -> do
|
||||
(g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId
|
||||
connectionStats <- mapM (withAgent . flip getConnectionServers) (memberConnId m)
|
||||
@@ -2570,7 +2569,7 @@ processChatCommand vr nm = \case
|
||||
let memberSupportChats = filter (isJust . supportChat) members
|
||||
pure $ CRMemberSupportChats user gInfo memberSupportChats
|
||||
APIListGroups userId contactId_ search_ -> withUserId userId $ \user ->
|
||||
CRGroupsList user <$> withFastStore' (\db -> getUserGroupsWithSummary db vr user contactId_ search_)
|
||||
CRGroupsList user <$> withFastStore' (\db -> getBaseGroupDetails db vr user contactId_ search_)
|
||||
ListGroups cName_ search_ -> withUser $ \user@User {userId} -> do
|
||||
ct_ <- forM cName_ $ \cName -> withFastStore $ \db -> getContactByName db vr user cName
|
||||
processChatCommand vr nm $ APIListGroups userId (contactId' <$> ct_) search_
|
||||
@@ -2923,7 +2922,7 @@ processChatCommand vr nm = \case
|
||||
ShowVersion -> do
|
||||
-- simplexmqCommitQ makes iOS builds crash m(
|
||||
let versionInfo = coreVersionInfo ""
|
||||
chatMigrations <- map upMigration <$> withFastStore' getCurrentMigrations
|
||||
chatMigrations <- map upMigration <$> withFastStore' (getCurrentMigrations Nothing)
|
||||
agentMigrations <- withAgent getAgentMigrations
|
||||
pure $ CRVersionInfo {versionInfo, chatMigrations, agentMigrations}
|
||||
DebugLocks -> lift $ do
|
||||
@@ -4502,8 +4501,8 @@ chatCommandP =
|
||||
<*> (A.space *> paginationByTimeP <|> pure (PTLast 5000))
|
||||
<*> (A.space *> jsonP <|> pure clqNoFilters)
|
||||
),
|
||||
"/_get chat " *> (APIGetChat <$> chatRefP <*> optional (" content=" *> strP) <* A.space <*> chatPaginationP <*> optional (" search=" *> stringP)),
|
||||
"/_get items " *> (APIGetChatItems <$> chatPaginationP <*> optional (" search=" *> stringP)),
|
||||
"/_get chat " *> (APIGetChat <$> chatRefP <*> optional (" content=" *> strP) <* A.space <*> chatPaginationP <*> optional (" search=" *> textP)),
|
||||
"/_get items " *> (APIGetChatItems <$> chatPaginationP <*> optional (" search=" *> textP)),
|
||||
"/_get item info " *> (APIGetChatItemInfo <$> chatRefP <* A.space <*> A.decimal),
|
||||
"/_send " *> (APISendMessages <$> sendRefP <*> liveMessageP <*> sendMessageTTLP <*> (" json " *> jsonP <|> " text " *> composedMessagesTextP)),
|
||||
"/_create tag " *> (APICreateChatTag <$> jsonP),
|
||||
@@ -4660,8 +4659,8 @@ chatCommandP =
|
||||
"/clear " *> char_ '@' *> (ClearContact <$> displayNameP),
|
||||
("/members " <|> "/ms ") *> char_ '#' *> (ListMembers <$> displayNameP),
|
||||
"/member support chats #" *> (ListMemberSupportChats <$> displayNameP),
|
||||
"/_groups" *> (APIListGroups <$> A.decimal <*> optional (" @" *> A.decimal) <*> optional (A.space *> stringP)),
|
||||
("/groups" <|> "/gs") *> (ListGroups <$> optional (" @" *> displayNameP) <*> optional (A.space *> stringP)),
|
||||
"/_groups" *> (APIListGroups <$> A.decimal <*> optional (" @" *> A.decimal) <*> optional (A.space *> textP)),
|
||||
("/groups" <|> "/gs") *> (ListGroups <$> optional (" @" *> displayNameP) <*> optional (A.space *> textP)),
|
||||
"/_group_profile #" *> (APIUpdateGroupProfile <$> A.decimal <* A.space <*> jsonP),
|
||||
("/group_profile " <|> "/gp ") *> char_ '#' *> (UpdateGroupNames <$> displayNameP <* A.space <*> groupProfile),
|
||||
("/group_profile " <|> "/gp ") *> char_ '#' *> (ShowGroupProfile <$> displayNameP),
|
||||
@@ -4715,7 +4714,7 @@ chatCommandP =
|
||||
"/feed " *> (SendMessageBroadcast . MCText <$> msgTextP),
|
||||
("/chats" <|> "/cs") *> (LastChats <$> (" all" $> Nothing <|> Just <$> (A.space *> A.decimal <|> pure 20))),
|
||||
("/tail" <|> "/t") *> (LastMessages <$> optional (A.space *> chatNameP) <*> msgCountP <*> pure Nothing),
|
||||
("/search" <|> "/?") *> (LastMessages <$> optional (A.space *> chatNameP) <*> msgCountP <*> (Just <$> (A.space *> stringP))),
|
||||
("/search" <|> "/?") *> (LastMessages <$> optional (A.space *> chatNameP) <*> msgCountP <*> (Just <$> (A.space *> textP))),
|
||||
"/last_item_id" *> (LastChatItemId <$> optional (A.space *> chatNameP) <*> (A.space *> A.decimal <|> pure 0)),
|
||||
"/show" *> (ShowLiveItems <$> (A.space *> onOffP <|> pure True)),
|
||||
"/show " *> (ShowChatItem . Just <$> A.decimal),
|
||||
|
||||
@@ -16,7 +16,7 @@ module Simplex.Chat.Store.Connections
|
||||
getConnectionEntityViaShortLink,
|
||||
getContactConnEntityByConnReqHash,
|
||||
getConnectionsToSubscribe,
|
||||
unsetConnectionToSubscribe
|
||||
unsetConnectionToSubscribe,
|
||||
)
|
||||
where
|
||||
|
||||
@@ -143,7 +143,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -14,9 +14,9 @@ module Simplex.Chat.Store.ContactRequest
|
||||
setContactAcceptedXContactId,
|
||||
setBusinessChatAcceptedXContactId,
|
||||
setRequestSharedMsgIdForContact,
|
||||
setRequestSharedMsgIdForGroup
|
||||
setRequestSharedMsgIdForGroup,
|
||||
)
|
||||
where
|
||||
where
|
||||
|
||||
import Control.Monad
|
||||
import Control.Monad.Except
|
||||
@@ -24,11 +24,11 @@ import Control.Monad.IO.Class
|
||||
import Crypto.Random (ChaChaDRG)
|
||||
import Data.Int (Int64)
|
||||
import Data.Time.Clock (getCurrentTime)
|
||||
import Simplex.Chat.Protocol (businessChatsVersion, MsgContent)
|
||||
import Simplex.Chat.Protocol (MsgContent, businessChatsVersion)
|
||||
import Simplex.Chat.Store.Direct
|
||||
import Simplex.Chat.Store.Groups
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Store.Profiles
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Messaging.Agent.Protocol (InvitationId)
|
||||
@@ -47,21 +47,21 @@ import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
createOrUpdateContactRequest ::
|
||||
DB.Connection
|
||||
-> TVar ChaChaDRG
|
||||
-> VersionRangeChat
|
||||
-> User
|
||||
-> Int64
|
||||
-> UserContactLink
|
||||
-> Bool
|
||||
-> InvitationId
|
||||
-> VersionRangeChat
|
||||
-> Profile
|
||||
-> Maybe XContactId
|
||||
-> Maybe SharedMsgId
|
||||
-> Maybe (SharedMsgId, MsgContent)
|
||||
-> PQSupport
|
||||
-> ExceptT StoreError IO RequestStage
|
||||
DB.Connection ->
|
||||
TVar ChaChaDRG ->
|
||||
VersionRangeChat ->
|
||||
User ->
|
||||
Int64 ->
|
||||
UserContactLink ->
|
||||
Bool ->
|
||||
InvitationId ->
|
||||
VersionRangeChat ->
|
||||
Profile ->
|
||||
Maybe XContactId ->
|
||||
Maybe SharedMsgId ->
|
||||
Maybe (SharedMsgId, MsgContent) ->
|
||||
PQSupport ->
|
||||
ExceptT StoreError IO RequestStage
|
||||
createOrUpdateContactRequest
|
||||
db
|
||||
gVar
|
||||
@@ -86,61 +86,62 @@ createOrUpdateContactRequest
|
||||
Just ct -> do
|
||||
cr <- liftIO $ getContactRequestByXContactId xContactId
|
||||
pure $ RSAcceptedRequest cr (REContact ct)
|
||||
Nothing -> liftIO (getAcceptedBusinessChat xContactId) >>= \case
|
||||
Just gInfo@GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do
|
||||
clientMember <- getGroupMemberByMemberId db vr user gInfo customerId
|
||||
cr <- liftIO $ getContactRequestByXContactId xContactId
|
||||
pure $ RSAcceptedRequest cr (REBusinessChat gInfo clientMember)
|
||||
Just GroupInfo {businessChat = Nothing} -> throwError SEInvalidBusinessChatContactRequest
|
||||
-- 2) if no legacy accepted contact or business chat was found, next we try to find an existing request
|
||||
Nothing ->
|
||||
liftIO (getContactRequestByXContactId xContactId) >>= \case
|
||||
-- 3a) if request was found, we update it
|
||||
Just cr -> updateContactRequest cr
|
||||
-- 3b) if no request was found, we create a new contact request
|
||||
Nothing -> createContactRequest
|
||||
where
|
||||
getAcceptedContact :: XContactId -> IO (Maybe Contact)
|
||||
getAcceptedContact xContactId = do
|
||||
ct_ <-
|
||||
maybeFirstRow (toContact vr user []) $
|
||||
Nothing ->
|
||||
liftIO (getAcceptedBusinessChat xContactId) >>= \case
|
||||
Just gInfo@GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do
|
||||
clientMember <- getGroupMemberByMemberId db vr user gInfo customerId
|
||||
cr <- liftIO $ getContactRequestByXContactId xContactId
|
||||
pure $ RSAcceptedRequest cr (REBusinessChat gInfo clientMember)
|
||||
Just GroupInfo {businessChat = Nothing} -> throwError SEInvalidBusinessChatContactRequest
|
||||
-- 2) if no legacy accepted contact or business chat was found, next we try to find an existing request
|
||||
Nothing ->
|
||||
liftIO (getContactRequestByXContactId xContactId) >>= \case
|
||||
-- 3a) if request was found, we update it
|
||||
Just cr -> updateContactRequest cr
|
||||
-- 3b) if no request was found, we create a new contact request
|
||||
Nothing -> createContactRequest
|
||||
where
|
||||
getAcceptedContact :: XContactId -> IO (Maybe Contact)
|
||||
getAcceptedContact xContactId = do
|
||||
ct_ <-
|
||||
maybeFirstRow (toContact vr user []) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, 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,
|
||||
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,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, xContactId)
|
||||
mapM (addDirectChatTags db) ct_
|
||||
getAcceptedBusinessChat :: XContactId -> IO (Maybe GroupInfo)
|
||||
getAcceptedBusinessChat xContactId = do
|
||||
g_ <-
|
||||
maybeFirstRow (toGroupInfo vr userContactId []) $
|
||||
DB.query
|
||||
db
|
||||
(groupInfoQuery <> " WHERE g.business_xcontact_id = ? AND g.user_id = ? AND mu.contact_id = ?")
|
||||
(xContactId, userId, userContactId)
|
||||
mapM (addGroupChatTags db) g_
|
||||
getContactRequestByXContactId :: XContactId -> IO (Maybe UserContactRequest)
|
||||
getContactRequestByXContactId xContactId =
|
||||
maybeFirstRow toContactRequest $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, 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,
|
||||
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,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, xContactId)
|
||||
mapM (addDirectChatTags db) ct_
|
||||
getAcceptedBusinessChat :: XContactId -> IO (Maybe GroupInfo)
|
||||
getAcceptedBusinessChat xContactId = do
|
||||
g_ <-
|
||||
maybeFirstRow (toGroupInfo vr userContactId []) $
|
||||
DB.query
|
||||
db
|
||||
(groupInfoQuery <> " WHERE g.business_xcontact_id = ? AND g.user_id = ? AND mu.contact_id = ?")
|
||||
(xContactId, userId, userContactId)
|
||||
mapM (addGroupChatTags db) g_
|
||||
getContactRequestByXContactId :: XContactId -> IO (Maybe UserContactRequest)
|
||||
getContactRequestByXContactId xContactId =
|
||||
maybeFirstRow toContactRequest $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||
@@ -154,79 +155,79 @@ createOrUpdateContactRequest
|
||||
AND cr.xcontact_id = ?
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, xContactId)
|
||||
createContactRequest :: ExceptT StoreError IO RequestStage
|
||||
createContactRequest = do
|
||||
currentTs <- liftIO $ getCurrentTime
|
||||
ExceptT $ withLocalDisplayName db userId displayName $ \ldn -> runExceptT $ do
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(displayName, fullName, shortDescr, image, contactLink, userId, preferences, currentTs, currentTs)
|
||||
profileId <- liftIO $ insertedRowId db
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
(userId, xContactId)
|
||||
createContactRequest :: ExceptT StoreError IO RequestStage
|
||||
createContactRequest = do
|
||||
currentTs <- liftIO $ getCurrentTime
|
||||
ExceptT $ withLocalDisplayName db userId displayName $ \ldn -> runExceptT $ do
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(displayName, fullName, shortDescr, image, contactLink, userId, preferences, currentTs, currentTs)
|
||||
profileId <- liftIO $ insertedRowId db
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO contact_requests
|
||||
(user_contact_link_id, agent_invitation_id, peer_chat_min_version, peer_chat_max_version, contact_profile_id, local_display_name, user_id,
|
||||
created_at, updated_at, xcontact_id, welcome_shared_msg_id, request_shared_msg_id, pq_support)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (uclId, Binary invId, minV, maxV, profileId, ldn, userId)
|
||||
:. (currentTs, currentTs, xContactId_, welcomeMsgId_, fst <$> requestMsg_, pqSup)
|
||||
)
|
||||
contactRequestId <- liftIO $ insertedRowId db
|
||||
createRequestEntity ldn profileId contactRequestId currentTs
|
||||
where
|
||||
createRequestEntity ldn profileId contactRequestId currentTs
|
||||
| businessAddress =
|
||||
if isSimplexTeam && maxV < businessChatsVersion
|
||||
then createContact'
|
||||
else createBusinessChat
|
||||
| otherwise = createContact'
|
||||
where
|
||||
createContact' = do
|
||||
let ctUserPreferences = newContactUserPrefs user profile
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, contact_request_id) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(profileId, ctUserPreferences, ldn, userId, currentTs, currentTs, currentTs, BI True, contactRequestId)
|
||||
contactId <- liftIO $ insertedRowId db
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contact_requests SET contact_id = ? WHERE contact_request_id = ?"
|
||||
(contactId, contactRequestId)
|
||||
ucr <- getContactRequest db user contactRequestId
|
||||
ct <- getContact db vr user contactId
|
||||
pure $ RSCurrentRequest Nothing ucr (Just $ REContact ct)
|
||||
createBusinessChat = do
|
||||
let groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs $ preferences' user
|
||||
(gInfo@GroupInfo {groupId}, clientMember) <-
|
||||
createBusinessRequestGroup db vr gVar user cReqChatVRange profile profileId ldn groupPreferences
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contact_requests SET business_group_id = ? WHERE contact_request_id = ?"
|
||||
(groupId, contactRequestId)
|
||||
ucr <- getContactRequest db user contactRequestId
|
||||
pure $ RSCurrentRequest Nothing ucr (Just $ REBusinessChat gInfo clientMember)
|
||||
updateContactRequest :: UserContactRequest -> ExceptT StoreError IO RequestStage
|
||||
updateContactRequest ucr@UserContactRequest {contactRequestId, contactId_, localDisplayName = oldLdn, profile = Profile {displayName = oldDisplayName}} = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
liftIO $ updateProfile currentTs
|
||||
updateRequest currentTs
|
||||
ucr' <- getContactRequest db user contactRequestId
|
||||
re_ <- getRequestEntity ucr'
|
||||
pure $ RSCurrentRequest (Just ucr) ucr' re_
|
||||
where
|
||||
updateProfile currentTs =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
( (uclId, Binary invId, minV, maxV, profileId, ldn, userId)
|
||||
:. (currentTs, currentTs, xContactId_, welcomeMsgId_, fst <$> requestMsg_, pqSup)
|
||||
)
|
||||
contactRequestId <- liftIO $ insertedRowId db
|
||||
createRequestEntity ldn profileId contactRequestId currentTs
|
||||
where
|
||||
createRequestEntity ldn profileId contactRequestId currentTs
|
||||
| businessAddress =
|
||||
if isSimplexTeam && maxV < businessChatsVersion
|
||||
then createContact'
|
||||
else createBusinessChat
|
||||
| otherwise = createContact'
|
||||
where
|
||||
createContact' = do
|
||||
let ctUserPreferences = newContactUserPrefs user profile
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contacts (contact_profile_id, user_preferences, local_display_name, user_id, created_at, updated_at, chat_ts, contact_used, contact_request_id) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(profileId, ctUserPreferences, ldn, userId, currentTs, currentTs, currentTs, BI True, contactRequestId)
|
||||
contactId <- liftIO $ insertedRowId db
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contact_requests SET contact_id = ? WHERE contact_request_id = ?"
|
||||
(contactId, contactRequestId)
|
||||
ucr <- getContactRequest db user contactRequestId
|
||||
ct <- getContact db vr user contactId
|
||||
pure $ RSCurrentRequest Nothing ucr (Just $ REContact ct)
|
||||
createBusinessChat = do
|
||||
let groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs $ preferences' user
|
||||
(gInfo@GroupInfo {groupId}, clientMember) <-
|
||||
createBusinessRequestGroup db vr gVar user cReqChatVRange profile profileId ldn groupPreferences
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contact_requests SET business_group_id = ? WHERE contact_request_id = ?"
|
||||
(groupId, contactRequestId)
|
||||
ucr <- getContactRequest db user contactRequestId
|
||||
pure $ RSCurrentRequest Nothing ucr (Just $ REBusinessChat gInfo clientMember)
|
||||
updateContactRequest :: UserContactRequest -> ExceptT StoreError IO RequestStage
|
||||
updateContactRequest ucr@UserContactRequest {contactRequestId, contactId_, localDisplayName = oldLdn, profile = Profile {displayName = oldDisplayName}} = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
liftIO $ updateProfile currentTs
|
||||
updateRequest currentTs
|
||||
ucr' <- getContactRequest db user contactRequestId
|
||||
re_ <- getRequestEntity ucr'
|
||||
pure $ RSCurrentRequest (Just ucr) ucr' re_
|
||||
where
|
||||
updateProfile currentTs =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE contact_profiles
|
||||
SET display_name = ?,
|
||||
full_name = ?,
|
||||
@@ -241,21 +242,20 @@ createOrUpdateContactRequest
|
||||
AND contact_request_id = ?
|
||||
)
|
||||
|]
|
||||
(displayName, fullName, shortDescr, image, contactLink, currentTs, userId, contactRequestId)
|
||||
updateRequest currentTs =
|
||||
if displayName == oldDisplayName
|
||||
then
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
(displayName, fullName, shortDescr, image, contactLink, currentTs, userId, contactRequestId)
|
||||
updateRequest currentTs =
|
||||
if displayName == oldDisplayName
|
||||
then
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE contact_requests
|
||||
SET agent_invitation_id = ?, pq_support = ?, peer_chat_min_version = ?, peer_chat_max_version = ?, updated_at = ?
|
||||
WHERE user_id = ? AND contact_request_id = ?
|
||||
|]
|
||||
(Binary invId, pqSup, minV, maxV, currentTs, userId, contactRequestId)
|
||||
else
|
||||
ExceptT $ withLocalDisplayName db userId displayName $ \ldn -> runExceptT $ do
|
||||
(Binary invId, pqSup, minV, maxV, currentTs, userId, contactRequestId)
|
||||
else ExceptT $ withLocalDisplayName db userId displayName $ \ldn -> runExceptT $ do
|
||||
liftIO $ do
|
||||
DB.execute
|
||||
db
|
||||
@@ -276,21 +276,21 @@ createOrUpdateContactRequest
|
||||
|]
|
||||
(ldn, currentTs, contactId)
|
||||
safeDeleteLDN db user oldLdn
|
||||
getRequestEntity :: UserContactRequest -> ExceptT StoreError IO (Maybe RequestEntity)
|
||||
getRequestEntity UserContactRequest {contactRequestId, contactId_, businessGroupId_} =
|
||||
case (contactId_, businessGroupId_) of
|
||||
(Just contactId, Nothing) -> do
|
||||
ct <- getContact db vr user contactId
|
||||
pure $ Just (REContact ct)
|
||||
(Nothing, Just businessGroupId) -> do
|
||||
gInfo <- getGroupInfo db vr user businessGroupId
|
||||
case gInfo of
|
||||
GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do
|
||||
clientMember <- getGroupMemberByMemberId db vr user gInfo customerId
|
||||
pure $ Just (REBusinessChat gInfo clientMember)
|
||||
_ -> throwError SEInvalidBusinessChatContactRequest
|
||||
(Nothing, Nothing) -> pure Nothing
|
||||
_ -> throwError $ SEInvalidContactRequestEntity contactRequestId
|
||||
getRequestEntity :: UserContactRequest -> ExceptT StoreError IO (Maybe RequestEntity)
|
||||
getRequestEntity UserContactRequest {contactRequestId, contactId_, businessGroupId_} =
|
||||
case (contactId_, businessGroupId_) of
|
||||
(Just contactId, Nothing) -> do
|
||||
ct <- getContact db vr user contactId
|
||||
pure $ Just (REContact ct)
|
||||
(Nothing, Just businessGroupId) -> do
|
||||
gInfo <- getGroupInfo db vr user businessGroupId
|
||||
case gInfo of
|
||||
GroupInfo {businessChat = Just BusinessChatInfo {customerId}} -> do
|
||||
clientMember <- getGroupMemberByMemberId db vr user gInfo customerId
|
||||
pure $ Just (REBusinessChat gInfo clientMember)
|
||||
_ -> throwError SEInvalidBusinessChatContactRequest
|
||||
(Nothing, Nothing) -> pure Nothing
|
||||
_ -> throwError $ SEInvalidContactRequestEntity contactRequestId
|
||||
|
||||
setContactAcceptedXContactId :: DB.Connection -> Contact -> XContactId -> IO ()
|
||||
setContactAcceptedXContactId db Contact {contactId} xContactId =
|
||||
|
||||
@@ -70,9 +70,7 @@ module Simplex.Chat.Store.Groups
|
||||
cleanupHostGroupLinkConn,
|
||||
deleteGroup,
|
||||
getUserGroupsToSubscribe,
|
||||
getUserGroupDetails,
|
||||
getUserGroupsWithSummary,
|
||||
getGroupSummary,
|
||||
getBaseGroupDetails,
|
||||
getContactGroupPreferences,
|
||||
getGroupInvitation,
|
||||
createNewContactMember,
|
||||
@@ -170,6 +168,7 @@ import Data.List (partition, sortOn)
|
||||
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing)
|
||||
import Data.Ord (Down (..))
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Simplex.Chat.Messages
|
||||
@@ -362,6 +361,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
groupSummary = GroupSummary 1,
|
||||
customData = Nothing,
|
||||
membersRequireAttention = 0,
|
||||
viaGroupLinkUri = Nothing
|
||||
@@ -436,6 +436,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
groupSummary = GroupSummary 2,
|
||||
customData = Nothing,
|
||||
membersRequireAttention = 0,
|
||||
viaGroupLinkUri = Nothing
|
||||
@@ -934,65 +935,21 @@ getUserGroupsToSubscribe db user@User {userId} = do
|
||||
groupIds <- map fromOnly <$> DB.query db "SELECT group_id FROM groups WHERE user_id = ?" (Only userId)
|
||||
rights <$> mapM (runExceptT . getGroupToSubscribe db user) groupIds
|
||||
|
||||
getUserGroupDetails :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo]
|
||||
getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
g_ <-
|
||||
map (toGroupInfo vr userContactId [])
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.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.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
FROM groups g
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu USING (group_id)
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || ? || '%'
|
||||
)
|
||||
|]
|
||||
(userId, userContactId, search, search, search, search)
|
||||
mapM (addGroupChatTags db) g_
|
||||
getBaseGroupDetails :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe Text -> IO [GroupInfo]
|
||||
getBaseGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
map (toGroupInfo vr userContactId [])
|
||||
<$> DB.query db (groupInfoQuery <> " " <> condition) (userId, userContactId, search, search, search, search)
|
||||
where
|
||||
search = maybe "" (map toLower) search_
|
||||
|
||||
getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfoSummary]
|
||||
getUserGroupsWithSummary db vr user _contactId_ search_ = do
|
||||
gs <- getUserGroupDetails db vr user _contactId_ search_
|
||||
forM gs $ \g@GroupInfo {groupId} -> do
|
||||
s <- getGroupSummary db user groupId
|
||||
link_ <- eitherToMaybe <$> runExceptT (getGroupLink db user g)
|
||||
pure $ GIS g s link_
|
||||
|
||||
-- the statuses on non-current members should match memberCurrent' function
|
||||
getGroupSummary :: DB.Connection -> User -> GroupId -> IO GroupSummary
|
||||
getGroupSummary db User {userId} groupId = do
|
||||
currentMembers_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT count (m.group_member_id)
|
||||
FROM groups g
|
||||
JOIN group_members m USING (group_id)
|
||||
WHERE g.user_id = ?
|
||||
AND g.group_id = ?
|
||||
AND m.member_status NOT IN (?,?,?,?,?)
|
||||
|]
|
||||
(userId, groupId, GSMemRejected, GSMemRemoved, GSMemLeft, GSMemUnknown, GSMemInvited)
|
||||
pure GroupSummary {currentMembers = fromMaybe 0 currentMembers_}
|
||||
condition =
|
||||
[sql|
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || ? || '%'
|
||||
)
|
||||
|]
|
||||
search = maybe "" (T.map toLower) search_
|
||||
|
||||
getContactGroupPreferences :: DB.Connection -> User -> Contact -> IO [(GroupMemberRole, FullGroupPreferences)]
|
||||
getContactGroupPreferences db User {userId} Contact {contactId} = do
|
||||
@@ -1915,7 +1872,7 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -2590,7 +2547,32 @@ createMemberContact
|
||||
quotaErrCounter = 0
|
||||
}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, preparedContact = Nothing, contactRequestId = Nothing, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, groupDirectInv = Nothing, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
pure
|
||||
Contact
|
||||
{ contactId,
|
||||
localDisplayName,
|
||||
profile = memberProfile,
|
||||
activeConn = Just ctConn,
|
||||
viaGroup = Nothing,
|
||||
contactUsed = True,
|
||||
contactStatus = CSActive,
|
||||
chatSettings = defaultChatSettings,
|
||||
userPreferences,
|
||||
mergedPreferences,
|
||||
createdAt = currentTs,
|
||||
updatedAt = currentTs,
|
||||
chatTs = Just currentTs,
|
||||
preparedContact = Nothing,
|
||||
contactRequestId = Nothing,
|
||||
contactGroupMemberId = Just groupMemberId,
|
||||
contactGrpInvSent = False,
|
||||
groupDirectInv = Nothing,
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
chatDeleted = False,
|
||||
customData = Nothing
|
||||
}
|
||||
|
||||
getMemberContact :: DB.Connection -> VersionRangeChat -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact db vr user contactId = do
|
||||
|
||||
@@ -1167,7 +1167,7 @@ checkContactHasItems db User {userId} Contact {contactId} =
|
||||
"SELECT EXISTS (SELECT 1 FROM chat_items WHERE user_id = ? AND contact_id = ?)"
|
||||
(userId, contactId)
|
||||
|
||||
getDirectChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe Text -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChat db vr user contactId pagination search_ = do
|
||||
let search = fromMaybe "" search_
|
||||
ct <- getContact db vr user contactId
|
||||
@@ -1177,18 +1177,18 @@ getDirectChat db vr user contactId pagination search_ = do
|
||||
CPBefore beforeId count -> (,Nothing) <$> getDirectChatBefore_ db user ct beforeId count search
|
||||
CPAround aroundId count -> getDirectChatAround_ db user ct aroundId count search
|
||||
CPInitial count -> do
|
||||
unless (null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
unless (T.null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
getDirectChatInitial_ db user ct count
|
||||
|
||||
-- the last items in reverse order (the last item in the conversation is the first in the returned list)
|
||||
getDirectChatLast_ :: DB.Connection -> User -> Contact -> Int -> String -> IO (Chat 'CTDirect)
|
||||
getDirectChatLast_ :: DB.Connection -> User -> Contact -> Int -> Text -> IO (Chat 'CTDirect)
|
||||
getDirectChatLast_ db user ct count search = do
|
||||
ciIds <- getDirectChatItemIdsLast_ db user ct count search
|
||||
ts <- getCurrentTime
|
||||
cis <- mapM (safeGetDirectItem db user ct ts) ciIds
|
||||
pure $ Chat (DirectChat ct) (reverse cis) emptyChatStats
|
||||
|
||||
getDirectChatItemIdsLast_ :: DB.Connection -> User -> Contact -> Int -> String -> IO [ChatItemId]
|
||||
getDirectChatItemIdsLast_ :: DB.Connection -> User -> Contact -> Int -> Text -> IO [ChatItemId]
|
||||
getDirectChatItemIdsLast_ db User {userId} Contact {contactId} count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1245,7 +1245,7 @@ getDirectChatItemLast db user@User {userId} contactId = do
|
||||
(userId, contactId)
|
||||
getDirectChatItem db user contactId chatItemId
|
||||
|
||||
getDirectChatAfter_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChatAfter_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChatAfter_ db user ct@Contact {contactId} afterId count search = do
|
||||
afterCI <- getDirectChatItem db user contactId afterId
|
||||
ciIds <- liftIO $ getDirectCIsAfter_ db user ct afterCI count search
|
||||
@@ -1253,7 +1253,7 @@ getDirectChatAfter_ db user ct@Contact {contactId} afterId count search = do
|
||||
cis <- liftIO $ mapM (safeGetDirectItem db user ct ts) ciIds
|
||||
pure $ Chat (DirectChat ct) cis emptyChatStats
|
||||
|
||||
getDirectCIsAfter_ :: DB.Connection -> User -> Contact -> CChatItem 'CTDirect -> Int -> String -> IO [ChatItemId]
|
||||
getDirectCIsAfter_ :: DB.Connection -> User -> Contact -> CChatItem 'CTDirect -> Int -> Text -> IO [ChatItemId]
|
||||
getDirectCIsAfter_ db User {userId} Contact {contactId} afterCI count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1268,7 +1268,7 @@ getDirectCIsAfter_ db User {userId} Contact {contactId} afterCI count search =
|
||||
|]
|
||||
(userId, contactId, search, ciCreatedAt afterCI, ciCreatedAt afterCI, cChatItemId afterCI, count)
|
||||
|
||||
getDirectChatBefore_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChatBefore_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTDirect)
|
||||
getDirectChatBefore_ db user ct@Contact {contactId} beforeId count search = do
|
||||
beforeCI <- getDirectChatItem db user contactId beforeId
|
||||
ciIds <- liftIO $ getDirectCIsBefore_ db user ct beforeCI count search
|
||||
@@ -1276,7 +1276,7 @@ getDirectChatBefore_ db user ct@Contact {contactId} beforeId count search = do
|
||||
cis <- liftIO $ mapM (safeGetDirectItem db user ct ts) ciIds
|
||||
pure $ Chat (DirectChat ct) (reverse cis) emptyChatStats
|
||||
|
||||
getDirectCIsBefore_ :: DB.Connection -> User -> Contact -> CChatItem 'CTDirect -> Int -> String -> IO [ChatItemId]
|
||||
getDirectCIsBefore_ :: DB.Connection -> User -> Contact -> CChatItem 'CTDirect -> Int -> Text -> IO [ChatItemId]
|
||||
getDirectCIsBefore_ db User {userId} Contact {contactId} beforeCI count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1291,12 +1291,12 @@ getDirectCIsBefore_ db User {userId} Contact {contactId} beforeCI count search =
|
||||
|]
|
||||
(userId, contactId, search, ciCreatedAt beforeCI, ciCreatedAt beforeCI, cChatItemId beforeCI, count)
|
||||
|
||||
getDirectChatAround_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChatAround_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChatAround_ db user ct aroundId count search = do
|
||||
stats <- liftIO $ getContactStats_ db user ct
|
||||
getDirectChatAround' db user ct aroundId count search stats
|
||||
|
||||
getDirectChatAround' :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> String -> ChatStats -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChatAround' :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> Text -> ChatStats -> ExceptT StoreError IO (Chat 'CTDirect, Maybe NavigationInfo)
|
||||
getDirectChatAround' db user ct@Contact {contactId} aroundId count search stats = do
|
||||
aroundCI <- getDirectChatItem db user contactId aroundId
|
||||
beforeIds <- liftIO $ getDirectCIsBefore_ db user ct aroundCI count search
|
||||
@@ -1404,7 +1404,7 @@ getContactNavInfo_ db User {userId} Contact {contactId} afterCI = do
|
||||
:. (userId, contactId, ciCreatedAt afterCI, cChatItemId afterCI)
|
||||
)
|
||||
|
||||
getGroupChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> Maybe GroupChatScope -> Maybe MsgContentTag -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> Maybe GroupChatScope -> Maybe MsgContentTag -> ChatPagination -> Maybe Text -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChat db vr user groupId scope_ contentFilter pagination search_ = do
|
||||
let search = fromMaybe "" search_
|
||||
g <- getGroupInfo db vr user groupId
|
||||
@@ -1415,7 +1415,7 @@ getGroupChat db vr user groupId scope_ contentFilter pagination search_ = do
|
||||
CPBefore beforeId count -> (,Nothing) <$> getGroupChatBefore_ db user g scopeInfo contentFilter beforeId count search
|
||||
CPAround aroundId count -> getGroupChatAround_ db user g scopeInfo contentFilter aroundId count search
|
||||
CPInitial count -> do
|
||||
unless (null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
unless (T.null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
getGroupChatInitial_ db user g scopeInfo contentFilter count
|
||||
|
||||
getCreateGroupChatScopeInfo :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> GroupChatScope -> ExceptT StoreError IO GroupChatScopeInfo
|
||||
@@ -1466,7 +1466,7 @@ getGroupChatScopeForItem_ db itemId =
|
||||
(Nothing, Nothing) -> Nothing
|
||||
(Nothing, Just _) -> Nothing -- shouldn't happen
|
||||
|
||||
getGroupChatLast_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> Int -> String -> ChatStats -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatLast_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> Int -> Text -> ChatStats -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatLast_ db user g scopeInfo_ contentFilter count search stats = do
|
||||
ciIds <- getGroupChatItemIDs db user g scopeInfo_ contentFilter GRLast count search
|
||||
ts <- liftIO getCurrentTime
|
||||
@@ -1475,7 +1475,7 @@ getGroupChatLast_ db user g scopeInfo_ contentFilter count search stats = do
|
||||
|
||||
data GroupItemIDsRange = GRLast | GRAfter UTCTime ChatItemId | GRBefore UTCTime ChatItemId
|
||||
|
||||
getGroupChatItemIDs :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> GroupItemIDsRange -> Int -> String -> ExceptT StoreError IO [ChatItemId]
|
||||
getGroupChatItemIDs :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> GroupItemIDsRange -> Int -> Text -> ExceptT StoreError IO [ChatItemId]
|
||||
getGroupChatItemIDs db User {userId} GroupInfo {groupId} scopeInfo_ contentFilter range count search = case (scopeInfo_, contentFilter) of
|
||||
(Nothing, Nothing) ->
|
||||
liftIO $
|
||||
@@ -1520,7 +1520,7 @@ getGroupChatItemIDs db User {userId} GroupInfo {groupId} scopeInfo_ contentFilte
|
||||
orParams ts itemId = (p :. (Only ts) :. p :. (ts, itemId))
|
||||
rangeQuery :: ToRow p => Query -> p -> Query -> IO [ChatItemId]
|
||||
rangeQuery c p ob
|
||||
| null search = searchQuery "" ()
|
||||
| T.null search = searchQuery "" ()
|
||||
| otherwise = searchQuery " AND LOWER(item_text) LIKE '%' || LOWER(?) || '%' " (Only search)
|
||||
where
|
||||
searchQuery :: ToRow p' => Query -> p' -> IO [ChatItemId]
|
||||
@@ -1570,7 +1570,7 @@ getGroupMemberChatItemLast db user@User {userId} groupId groupMemberId = do
|
||||
(userId, groupId, groupMemberId)
|
||||
getGroupChatItem db user groupId chatItemId
|
||||
|
||||
getGroupChatAfter_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatAfter_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatAfter_ db user g@GroupInfo {groupId} scopeInfo contentFilter afterId count search = do
|
||||
afterCI <- getGroupChatItem db user groupId afterId
|
||||
let range = GRAfter (chatItemTs afterCI) (cChatItemId afterCI)
|
||||
@@ -1579,7 +1579,7 @@ getGroupChatAfter_ db user g@GroupInfo {groupId} scopeInfo contentFilter afterId
|
||||
cis <- liftIO $ mapM (safeGetGroupItem db user g ts) ciIds
|
||||
pure $ Chat (GroupChat g scopeInfo) cis emptyChatStats
|
||||
|
||||
getGroupChatBefore_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatBefore_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTGroup)
|
||||
getGroupChatBefore_ db user g@GroupInfo {groupId} scopeInfo contentFilter beforeId count search = do
|
||||
beforeCI <- getGroupChatItem db user groupId beforeId
|
||||
let range = GRBefore (chatItemTs beforeCI) (cChatItemId beforeCI)
|
||||
@@ -1588,12 +1588,12 @@ getGroupChatBefore_ db user g@GroupInfo {groupId} scopeInfo contentFilter before
|
||||
cis <- liftIO $ mapM (safeGetGroupItem db user g ts) ciIds
|
||||
pure $ Chat (GroupChat g scopeInfo) (reverse cis) emptyChatStats
|
||||
|
||||
getGroupChatAround_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChatAround_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChatAround_ db user g scopeInfo contentFilter aroundId count search = do
|
||||
stats <- getGroupStats_ db user g scopeInfo
|
||||
getGroupChatAround' db user g scopeInfo contentFilter aroundId count search stats
|
||||
|
||||
getGroupChatAround' :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> String -> ChatStats -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChatAround' :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> Text -> ChatStats -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChatAround' db user g scopeInfo contentFilter aroundId count search stats = do
|
||||
aroundCI <- getGroupCIWithReactions db user g aroundId
|
||||
let beforeRange = GRBefore (chatItemTs aroundCI) (cChatItemId aroundCI)
|
||||
@@ -1736,7 +1736,7 @@ getGroupNavInfo_ db User {userId} GroupInfo {groupId} afterCI = do
|
||||
:. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI)
|
||||
)
|
||||
|
||||
getLocalChat :: DB.Connection -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChat :: DB.Connection -> User -> Int64 -> ChatPagination -> Maybe Text -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChat db user folderId pagination search_ = do
|
||||
let search = fromMaybe "" search_
|
||||
nf <- getNoteFolder db user folderId
|
||||
@@ -1746,17 +1746,17 @@ getLocalChat db user folderId pagination search_ = do
|
||||
CPBefore beforeId count -> (,Nothing) <$> getLocalChatBefore_ db user nf beforeId count search
|
||||
CPAround aroundId count -> getLocalChatAround_ db user nf aroundId count search
|
||||
CPInitial count -> do
|
||||
unless (null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
unless (T.null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
getLocalChatInitial_ db user nf count
|
||||
|
||||
getLocalChatLast_ :: DB.Connection -> User -> NoteFolder -> Int -> String -> IO (Chat 'CTLocal)
|
||||
getLocalChatLast_ :: DB.Connection -> User -> NoteFolder -> Int -> Text -> IO (Chat 'CTLocal)
|
||||
getLocalChatLast_ db user nf count search = do
|
||||
ciIds <- getLocalChatItemIdsLast_ db user nf count search
|
||||
ts <- getCurrentTime
|
||||
cis <- mapM (safeGetLocalItem db user nf ts) ciIds
|
||||
pure $ Chat (LocalChat nf) (reverse cis) emptyChatStats
|
||||
|
||||
getLocalChatItemIdsLast_ :: DB.Connection -> User -> NoteFolder -> Int -> String -> IO [ChatItemId]
|
||||
getLocalChatItemIdsLast_ :: DB.Connection -> User -> NoteFolder -> Int -> Text -> IO [ChatItemId]
|
||||
getLocalChatItemIdsLast_ db User {userId} NoteFolder {noteFolderId} count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1797,7 +1797,7 @@ safeToLocalItem currentTs itemId = \case
|
||||
file = Nothing
|
||||
}
|
||||
|
||||
getLocalChatAfter_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTLocal)
|
||||
getLocalChatAfter_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTLocal)
|
||||
getLocalChatAfter_ db user nf@NoteFolder {noteFolderId} afterId count search = do
|
||||
afterCI <- getLocalChatItem db user noteFolderId afterId
|
||||
ciIds <- liftIO $ getLocalCIsAfter_ db user nf afterCI count search
|
||||
@@ -1805,7 +1805,7 @@ getLocalChatAfter_ db user nf@NoteFolder {noteFolderId} afterId count search = d
|
||||
cis <- liftIO $ mapM (safeGetLocalItem db user nf ts) ciIds
|
||||
pure $ Chat (LocalChat nf) cis emptyChatStats
|
||||
|
||||
getLocalCIsAfter_ :: DB.Connection -> User -> NoteFolder -> CChatItem 'CTLocal -> Int -> String -> IO [ChatItemId]
|
||||
getLocalCIsAfter_ :: DB.Connection -> User -> NoteFolder -> CChatItem 'CTLocal -> Int -> Text -> IO [ChatItemId]
|
||||
getLocalCIsAfter_ db User {userId} NoteFolder {noteFolderId} afterCI count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1820,7 +1820,7 @@ getLocalCIsAfter_ db User {userId} NoteFolder {noteFolderId} afterCI count searc
|
||||
|]
|
||||
(userId, noteFolderId, search, ciCreatedAt afterCI, ciCreatedAt afterCI, cChatItemId afterCI, count)
|
||||
|
||||
getLocalChatBefore_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTLocal)
|
||||
getLocalChatBefore_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTLocal)
|
||||
getLocalChatBefore_ db user nf@NoteFolder {noteFolderId} beforeId count search = do
|
||||
beforeCI <- getLocalChatItem db user noteFolderId beforeId
|
||||
ciIds <- liftIO $ getLocalCIsBefore_ db user nf beforeCI count search
|
||||
@@ -1828,7 +1828,7 @@ getLocalChatBefore_ db user nf@NoteFolder {noteFolderId} beforeId count search =
|
||||
cis <- liftIO $ mapM (safeGetLocalItem db user nf ts) ciIds
|
||||
pure $ Chat (LocalChat nf) (reverse cis) emptyChatStats
|
||||
|
||||
getLocalCIsBefore_ :: DB.Connection -> User -> NoteFolder -> CChatItem 'CTLocal -> Int -> String -> IO [ChatItemId]
|
||||
getLocalCIsBefore_ :: DB.Connection -> User -> NoteFolder -> CChatItem 'CTLocal -> Int -> Text -> IO [ChatItemId]
|
||||
getLocalCIsBefore_ db User {userId} NoteFolder {noteFolderId} beforeCI count search =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
@@ -1843,12 +1843,12 @@ getLocalCIsBefore_ db User {userId} NoteFolder {noteFolderId} beforeCI count sea
|
||||
|]
|
||||
(userId, noteFolderId, search, ciCreatedAt beforeCI, ciCreatedAt beforeCI, cChatItemId beforeCI, count)
|
||||
|
||||
getLocalChatAround_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> String -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChatAround_ :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> Text -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChatAround_ db user nf aroundId count search = do
|
||||
stats <- liftIO $ getLocalStats_ db user nf
|
||||
getLocalChatAround' db user nf aroundId count search stats
|
||||
|
||||
getLocalChatAround' :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> String -> ChatStats -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChatAround' :: DB.Connection -> User -> NoteFolder -> ChatItemId -> Int -> Text -> ChatStats -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo)
|
||||
getLocalChatAround' db user nf@NoteFolder {noteFolderId} aroundId count search stats = do
|
||||
aroundCI <- getLocalChatItem db user noteFolderId aroundId
|
||||
beforeIds <- liftIO $ getLocalCIsBefore_ db user nf aroundCI count search
|
||||
@@ -2370,7 +2370,7 @@ toGroupChatItem
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
getAllChatItems :: DB.Connection -> VersionRangeChat -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
||||
getAllChatItems :: DB.Connection -> VersionRangeChat -> User -> ChatPagination -> Maybe Text -> ExceptT StoreError IO [AChatItem]
|
||||
getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
itemRefs <-
|
||||
rights . map toChatItemRef <$> case pagination of
|
||||
@@ -2379,7 +2379,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
CPBefore beforeId count -> liftIO . getAllChatItemsBefore_ beforeId count . aChatItemTs =<< getAChatItem_ beforeId
|
||||
CPAround aroundId count -> liftIO . getAllChatItemsAround_ aroundId count . aChatItemTs =<< getAChatItem_ aroundId
|
||||
CPInitial count -> do
|
||||
unless (null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
unless (T.null search) $ throwError $ SEInternalError "initial chat pagination doesn't support search"
|
||||
liftIO getFirstUnreadItemId_ >>= \case
|
||||
Just itemId -> liftIO . getAllChatItemsAround_ itemId count . aChatItemTs =<< getAChatItem_ itemId
|
||||
Nothing -> liftIO $ getAllChatItemsLast_ count
|
||||
|
||||
@@ -17,6 +17,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20250729_member_contact_requests
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250801_via_group_link_uri
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250802_chat_peer_type
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250813_delivery_tasks
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250919_group_summary
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
@@ -33,7 +34,8 @@ schemaMigrations =
|
||||
("20250729_member_contact_requests", m20250729_member_contact_requests, Just down_m20250729_member_contact_requests),
|
||||
("20250801_via_group_link_uri", m20250801_via_group_link_uri, Just down_m20250801_via_group_link_uri),
|
||||
("20250802_chat_peer_type", m20250802_chat_peer_type, Just down_m20250802_chat_peer_type),
|
||||
("20250813_delivery_tasks", m20250813_delivery_tasks, Just down_m20250813_delivery_tasks)
|
||||
("20250813_delivery_tasks", m20250813_delivery_tasks, Just down_m20250813_delivery_tasks),
|
||||
("20250919_group_summary", m20250919_group_summary, Just down_m20250919_group_summary)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20250919_group_summary where
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20250919_group_summary :: Text
|
||||
m20250919_group_summary =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE groups ADD COLUMN summary_current_members_count BIGINT NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_groups_summary_current_members_count ON groups(summary_current_members_count);
|
||||
|
||||
CREATE FUNCTION is_current_member(p_status TEXT) RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RETURN p_status IN (
|
||||
'introduced',
|
||||
'intro-inv',
|
||||
'accepted',
|
||||
'announced',
|
||||
'connected',
|
||||
'complete',
|
||||
'creator'
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
UPDATE groups g
|
||||
SET summary_current_members_count = COALESCE(c.cnt, 0)
|
||||
FROM (
|
||||
SELECT group_id, COUNT(group_member_id) AS cnt
|
||||
FROM group_members
|
||||
WHERE is_current_member(member_status) = TRUE
|
||||
GROUP BY group_id
|
||||
) c
|
||||
WHERE g.group_id = c.group_id;
|
||||
|
||||
CREATE FUNCTION on_group_members_insert_update_summary() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF is_current_member(NEW.member_status) THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count + 1
|
||||
WHERE group_id = NEW.group_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION on_group_members_delete_update_summary() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF is_current_member(OLD.member_status) THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count - 1
|
||||
WHERE group_id = OLD.group_id;
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION on_group_members_update_update_summary() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
was_active BOOLEAN;
|
||||
is_active BOOLEAN;
|
||||
BEGIN
|
||||
was_active := is_current_member(OLD.member_status);
|
||||
is_active := is_current_member(NEW.member_status);
|
||||
|
||||
IF was_active != is_active THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count +
|
||||
(CASE WHEN is_active THEN 1 ELSE -1 END)
|
||||
WHERE group_id = NEW.group_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER tr_group_members_insert_update_summary
|
||||
AFTER INSERT ON group_members
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION on_group_members_insert_update_summary();
|
||||
|
||||
CREATE TRIGGER tr_group_members_delete_update_summary
|
||||
AFTER DELETE ON group_members
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION on_group_members_delete_update_summary();
|
||||
|
||||
CREATE TRIGGER tr_group_members_update_update_summary
|
||||
AFTER UPDATE ON group_members
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION on_group_members_update_update_summary();
|
||||
|]
|
||||
|
||||
down_m20250919_group_summary :: Text
|
||||
down_m20250919_group_summary =
|
||||
T.pack
|
||||
[r|
|
||||
DROP TRIGGER tr_group_members_insert_update_summary ON group_members;
|
||||
DROP TRIGGER tr_group_members_delete_update_summary ON group_members;
|
||||
DROP TRIGGER tr_group_members_update_update_summary ON group_members;
|
||||
|
||||
DROP FUNCTION on_group_members_insert_update_summary;
|
||||
DROP FUNCTION on_group_members_delete_update_summary;
|
||||
DROP FUNCTION on_group_members_update_update_summary;
|
||||
|
||||
DROP FUNCTION is_current_member;
|
||||
|
||||
DROP INDEX idx_groups_summary_current_members_count;
|
||||
ALTER TABLE groups DROP COLUMN summary_current_members_count;
|
||||
|]
|
||||
@@ -15,6 +15,76 @@ SET row_security = off;
|
||||
CREATE SCHEMA test_chat_schema;
|
||||
|
||||
|
||||
|
||||
CREATE FUNCTION test_chat_schema.is_current_member(p_status text) RETURNS boolean
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN p_status IN (
|
||||
'introduced',
|
||||
'intro-inv',
|
||||
'accepted',
|
||||
'announced',
|
||||
'connected',
|
||||
'complete',
|
||||
'creator'
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
|
||||
CREATE FUNCTION test_chat_schema.on_group_members_delete_update_summary() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF is_current_member(OLD.member_status) THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count - 1
|
||||
WHERE group_id = OLD.group_id;
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
|
||||
CREATE FUNCTION test_chat_schema.on_group_members_insert_update_summary() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF is_current_member(NEW.member_status) THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count + 1
|
||||
WHERE group_id = NEW.group_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
|
||||
CREATE FUNCTION test_chat_schema.on_group_members_update_update_summary() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
was_active BOOLEAN;
|
||||
is_active BOOLEAN;
|
||||
BEGIN
|
||||
was_active := is_current_member(OLD.member_status);
|
||||
is_active := is_current_member(NEW.member_status);
|
||||
|
||||
IF was_active != is_active THEN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count +
|
||||
(CASE WHEN is_active THEN 1 ELSE -1 END)
|
||||
WHERE group_id = NEW.group_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
|
||||
@@ -716,7 +786,8 @@ CREATE TABLE test_chat_schema.groups (
|
||||
welcome_shared_msg_id bytea,
|
||||
request_shared_msg_id bytea,
|
||||
conn_link_prepared_connection smallint DEFAULT 0 NOT NULL,
|
||||
via_group_link_uri bytea
|
||||
via_group_link_uri bytea,
|
||||
summary_current_members_count bigint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -2064,6 +2135,10 @@ CREATE INDEX idx_groups_inv_queue_info ON test_chat_schema.groups USING btree (i
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_groups_summary_current_members_count ON test_chat_schema.groups USING btree (summary_current_members_count);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_groups_via_group_link_uri_hash ON test_chat_schema.groups USING btree (user_id, via_group_link_uri_hash);
|
||||
|
||||
|
||||
@@ -2248,6 +2323,18 @@ CREATE INDEX note_folders_user_id ON test_chat_schema.note_folders USING btree (
|
||||
|
||||
|
||||
|
||||
CREATE TRIGGER tr_group_members_delete_update_summary AFTER DELETE ON test_chat_schema.group_members FOR EACH ROW EXECUTE FUNCTION test_chat_schema.on_group_members_delete_update_summary();
|
||||
|
||||
|
||||
|
||||
CREATE TRIGGER tr_group_members_insert_update_summary AFTER INSERT ON test_chat_schema.group_members FOR EACH ROW EXECUTE FUNCTION test_chat_schema.on_group_members_insert_update_summary();
|
||||
|
||||
|
||||
|
||||
CREATE TRIGGER tr_group_members_update_update_summary AFTER UPDATE ON test_chat_schema.group_members FOR EACH ROW EXECUTE FUNCTION test_chat_schema.on_group_members_update_update_summary();
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.calls
|
||||
ADD CONSTRAINT calls_chat_item_id_fkey FOREIGN KEY (chat_item_id) REFERENCES test_chat_schema.chat_items(chat_item_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20250729_member_contact_requests
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250801_via_group_link_uri
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250802_chat_peer_type
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250813_delivery_tasks
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250919_group_summary
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -279,7 +280,8 @@ schemaMigrations =
|
||||
("20250729_member_contact_requests", m20250729_member_contact_requests, Just down_m20250729_member_contact_requests),
|
||||
("20250801_via_group_link_uri", m20250801_via_group_link_uri, Just down_m20250801_via_group_link_uri),
|
||||
("20250802_chat_peer_type", m20250802_chat_peer_type, Just down_m20250802_chat_peer_type),
|
||||
("20250813_delivery_tasks", m20250813_delivery_tasks, Just down_m20250813_delivery_tasks)
|
||||
("20250813_delivery_tasks", m20250813_delivery_tasks, Just down_m20250813_delivery_tasks),
|
||||
("20250919_group_summary", m20250919_group_summary, Just down_m20250919_group_summary)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -104,8 +104,6 @@ CREATE INDEX idx_delivery_tasks_next_for_job_scope_sender ON delivery_tasks(
|
||||
);
|
||||
CREATE INDEX idx_delivery_tasks_created_at ON delivery_tasks(created_at);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE delivery_jobs (
|
||||
delivery_job_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
@@ -135,8 +133,6 @@ CREATE INDEX idx_delivery_jobs_next ON delivery_jobs(
|
||||
);
|
||||
CREATE INDEX idx_delivery_jobs_created_at ON delivery_jobs(created_at);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE messages ADD COLUMN broker_ts TEXT;
|
||||
|]
|
||||
|
||||
@@ -145,8 +141,6 @@ down_m20250813_delivery_tasks =
|
||||
[sql|
|
||||
ALTER TABLE messages DROP COLUMN broker_ts;
|
||||
|
||||
|
||||
|
||||
DROP INDEX idx_delivery_jobs_group_id;
|
||||
DROP INDEX idx_delivery_jobs_job_scope_support_gm_id;
|
||||
DROP INDEX idx_delivery_jobs_single_sender_group_member_id;
|
||||
@@ -156,8 +150,6 @@ DROP INDEX idx_delivery_jobs_created_at;
|
||||
|
||||
DROP TABLE delivery_jobs;
|
||||
|
||||
|
||||
|
||||
DROP INDEX idx_delivery_tasks_group_id;
|
||||
DROP INDEX idx_delivery_tasks_job_scope_support_gm_id;
|
||||
DROP INDEX idx_delivery_tasks_sender_group_member_id;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250919_group_summary where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20250919_group_summary :: Query
|
||||
m20250919_group_summary =
|
||||
[sql|
|
||||
ALTER TABLE groups ADD COLUMN summary_current_members_count INTEGER NOT NULL DEFAULT 0;
|
||||
CREATE INDEX idx_groups_summary_current_members_count ON groups(summary_current_members_count);
|
||||
|
||||
CREATE TABLE group_member_status_predicates(
|
||||
member_status TEXT NOT NULL PRIMARY KEY,
|
||||
current_member INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
INSERT INTO group_member_status_predicates(member_status, current_member)
|
||||
VALUES
|
||||
('rejected', 0),
|
||||
('removed', 0),
|
||||
('left', 0),
|
||||
('deleted', 0),
|
||||
('unknown', 0),
|
||||
('invited', 0),
|
||||
('pending_approval', 0),
|
||||
('pending_review', 0),
|
||||
('introduced', 1),
|
||||
('intro-inv', 1),
|
||||
('accepted', 1),
|
||||
('announced', 1),
|
||||
('connected', 1),
|
||||
('complete', 1),
|
||||
('creator', 1);
|
||||
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = c.cnt
|
||||
FROM (
|
||||
SELECT m.group_id, COUNT(m.group_member_id) AS cnt
|
||||
FROM group_members m
|
||||
JOIN group_member_status_predicates p ON m.member_status = p.member_status
|
||||
WHERE p.current_member = 1
|
||||
GROUP BY m.group_id
|
||||
) AS c
|
||||
WHERE groups.group_id = c.group_id;
|
||||
|
||||
CREATE TRIGGER on_group_members_insert_update_summary
|
||||
AFTER INSERT ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count + 1
|
||||
WHERE group_id = NEW.group_id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER on_group_members_delete_update_summary
|
||||
AFTER DELETE ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = OLD.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count - 1
|
||||
WHERE group_id = OLD.group_id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER on_group_members_update_update_summary
|
||||
AFTER UPDATE ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = OLD.member_status AND current_member = 1)
|
||||
!= EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count +
|
||||
(
|
||||
CASE WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
THEN 1 ELSE -1 END
|
||||
)
|
||||
WHERE group_id = NEW.group_id;
|
||||
END;
|
||||
|]
|
||||
|
||||
down_m20250919_group_summary :: Query
|
||||
down_m20250919_group_summary =
|
||||
[sql|
|
||||
DROP TRIGGER on_group_members_insert_update_summary;
|
||||
DROP TRIGGER on_group_members_delete_update_summary;
|
||||
DROP TRIGGER on_group_members_update_update_summary;
|
||||
|
||||
DROP TABLE group_member_status_predicates;
|
||||
|
||||
DROP INDEX idx_groups_summary_current_members_count;
|
||||
ALTER TABLE groups DROP COLUMN summary_current_members_count;
|
||||
|]
|
||||
@@ -48,6 +48,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO groups
|
||||
@@ -57,6 +82,30 @@ Query:
|
||||
|
||||
Plan:
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, 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,
|
||||
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,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|
||||
Plan:
|
||||
SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?)
|
||||
SEARCH cp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
@@ -65,7 +114,7 @@ Query:
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -192,6 +241,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
@@ -201,6 +275,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO groups
|
||||
@@ -210,30 +309,6 @@ Query:
|
||||
|
||||
Plan:
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, 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,
|
||||
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,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|
||||
Plan:
|
||||
SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?)
|
||||
SEARCH cp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH c USING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
|
||||
Query:
|
||||
SELECT COUNT(1)
|
||||
FROM chat_items i
|
||||
@@ -374,6 +449,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
@@ -383,6 +483,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
@@ -392,6 +517,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO messages (
|
||||
@@ -851,6 +1001,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO groups
|
||||
@@ -1024,7 +1199,7 @@ Query:
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -1067,35 +1242,6 @@ SEARCH c USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group_member_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.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.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
FROM groups g
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu USING (group_id)
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || ? || '%'
|
||||
)
|
||||
|
||||
Plan:
|
||||
SEARCH mu USING INDEX idx_group_members_contact_id (contact_id=?)
|
||||
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT 1
|
||||
FROM group_member_intros
|
||||
@@ -1284,18 +1430,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH files USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT count (m.group_member_id)
|
||||
FROM groups g
|
||||
JOIN group_members m USING (group_id)
|
||||
WHERE g.user_id = ?
|
||||
AND g.group_id = ?
|
||||
AND m.member_status NOT IN (?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH m USING INDEX sqlite_autoindex_group_members_1 (group_id=?)
|
||||
|
||||
Query:
|
||||
SELECT ct.contact_id
|
||||
FROM contacts ct
|
||||
@@ -1625,6 +1759,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
@@ -1634,6 +1793,31 @@ Query:
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_single_sender_group_member_id (single_sender_group_member_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_sender_group_member_id (sender_group_member_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_job_scope_support_gm_id (job_scope_support_gm_id=?)
|
||||
SEARCH received_probes USING COVERING INDEX idx_received_probes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probe_hashes USING COVERING INDEX idx_sent_probe_hashes_group_member_id (group_member_id=?)
|
||||
SEARCH sent_probes USING COVERING INDEX idx_sent_probes_group_member_id (group_member_id=?)
|
||||
SEARCH group_snd_item_statuses USING COVERING INDEX idx_group_snd_item_statuses_group_member_id (group_member_id=?)
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_moderator_member_id (moderator_member_id=?)
|
||||
SEARCH chat_item_reactions USING COVERING INDEX idx_chat_item_reactions_group_member_id (group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_group_member_id (group_scope_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_item_deleted_by_group_member_id (item_deleted_by_group_member_id=?)
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_member_id (group_member_id=?)
|
||||
SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_group_member_id (group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_forwarded_by_group_member_id (forwarded_by_group_member_id=?)
|
||||
SEARCH messages USING COVERING INDEX idx_messages_author_group_member_id (author_group_member_id=?)
|
||||
SEARCH connections USING COVERING INDEX idx_connections_group_member_id (group_member_id=?)
|
||||
SEARCH rcv_files USING COVERING INDEX idx_rcv_files_group_member_id (group_member_id=?)
|
||||
SEARCH snd_files USING COVERING INDEX idx_snd_files_group_member_id (group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
|
||||
SEARCH group_member_intros USING COVERING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
|
||||
SEARCH group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id (invited_by_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_grp_direct_inv_from_group_member_id (grp_direct_inv_from_group_member_id=?)
|
||||
SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (contact_group_member_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO msg_deliveries
|
||||
@@ -4899,13 +5083,48 @@ Query:
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
JOIN group_members mu ON mu.group_id = g.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || ? || '%'
|
||||
)
|
||||
|
||||
Plan:
|
||||
SEARCH mu USING INDEX idx_group_members_contact_id (contact_id=?)
|
||||
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
JOIN group_members mu ON mu.group_id = g.group_id
|
||||
@@ -4925,13 +5144,14 @@ Query:
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
JOIN group_members mu ON mu.group_id = g.group_id
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
CREATE TABLE migrations(
|
||||
name TEXT NOT NULL,
|
||||
name TEXT NOT NULL PRIMARY KEY,
|
||||
ts TEXT NOT NULL,
|
||||
down TEXT,
|
||||
PRIMARY KEY(name)
|
||||
down TEXT
|
||||
);
|
||||
CREATE TABLE contact_profiles(
|
||||
-- remote user profile
|
||||
@@ -156,7 +155,8 @@ CREATE TABLE groups(
|
||||
welcome_shared_msg_id BLOB,
|
||||
request_shared_msg_id BLOB,
|
||||
conn_link_prepared_connection INTEGER NOT NULL DEFAULT 0,
|
||||
via_group_link_uri BLOB, -- received
|
||||
via_group_link_uri BLOB,
|
||||
summary_current_members_count INTEGER NOT NULL DEFAULT 0, -- received
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -724,6 +724,10 @@ CREATE TABLE delivery_jobs(
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
CREATE TABLE group_member_status_predicates(
|
||||
member_status TEXT NOT NULL PRIMARY KEY,
|
||||
current_member INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX contact_profiles_index ON contact_profiles(
|
||||
display_name,
|
||||
full_name
|
||||
@@ -1184,3 +1188,38 @@ CREATE INDEX idx_delivery_jobs_next ON delivery_jobs(
|
||||
job_status
|
||||
);
|
||||
CREATE INDEX idx_delivery_jobs_created_at ON delivery_jobs(created_at);
|
||||
CREATE INDEX idx_groups_summary_current_members_count ON groups(
|
||||
summary_current_members_count
|
||||
);
|
||||
CREATE TRIGGER on_group_members_insert_update_summary
|
||||
AFTER INSERT ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count + 1
|
||||
WHERE group_id = NEW.group_id;
|
||||
END;
|
||||
CREATE TRIGGER on_group_members_delete_update_summary
|
||||
AFTER DELETE ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = OLD.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count - 1
|
||||
WHERE group_id = OLD.group_id;
|
||||
END;
|
||||
CREATE TRIGGER on_group_members_update_update_summary
|
||||
AFTER UPDATE ON group_members
|
||||
FOR EACH ROW
|
||||
WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = OLD.member_status AND current_member = 1)
|
||||
!= EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
BEGIN
|
||||
UPDATE groups
|
||||
SET summary_current_members_count = summary_current_members_count +
|
||||
(
|
||||
CASE WHEN EXISTS (SELECT 1 FROM group_member_status_predicates WHERE member_status = NEW.member_status AND current_member = 1)
|
||||
THEN 1 ELSE -1 END
|
||||
)
|
||||
WHERE group_id = NEW.group_id;
|
||||
END;
|
||||
|
||||
@@ -37,7 +37,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), AConnShortLink (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionRequestUri, CreatedConnLink (..), UserId, connMode)
|
||||
import Simplex.Messaging.Agent.Protocol (AConnShortLink (..), AConnectionRequestUri (..), ACreatedConnLink (..), ConnId, ConnShortLink, ConnectionRequestUri, CreatedConnLink (..), UserId, connMode)
|
||||
import Simplex.Messaging.Agent.Store (AnyStoreError (..))
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
@@ -656,21 +656,22 @@ type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt,
|
||||
|
||||
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
|
||||
|
||||
type GroupMemberRow = (Int64, 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)
|
||||
|
||||
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
|
||||
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (uiThemes, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. userMemberRow) =
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (uiThemes, currentMembers, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. userMemberRow) =
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
|
||||
businessChat = toBusinessChatInfo businessRow
|
||||
preparedGroup = toPreparedGroup preparedGroupRow
|
||||
in GroupInfo {groupId, useRelays = False, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, customData, membersRequireAttention, viaGroupLinkUri}
|
||||
groupSummary = GroupSummary {currentMembers}
|
||||
in GroupInfo {groupId, useRelays = False, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, groupSummary, customData, membersRequireAttention, viaGroupLinkUri}
|
||||
|
||||
toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup
|
||||
toPreparedGroup = \case
|
||||
@@ -688,13 +689,14 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
|
||||
memberChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||
supportChat = case supportChatTs_ of
|
||||
Just chatTs ->
|
||||
Just GroupSupportChat {
|
||||
chatTs,
|
||||
unread = supportChatUnread,
|
||||
memberAttention = supportChatMemberAttention,
|
||||
mentions = supportChatMentions,
|
||||
lastMsgFromMemberTs = supportChatLastMsgFromMemberTs
|
||||
}
|
||||
Just
|
||||
GroupSupportChat
|
||||
{ chatTs,
|
||||
unread = supportChatUnread,
|
||||
memberAttention = supportChatMemberAttention,
|
||||
mentions = supportChatMentions,
|
||||
lastMsgFromMemberTs = supportChatLastMsgFromMemberTs
|
||||
}
|
||||
_ -> Nothing
|
||||
in GroupMember {..}
|
||||
|
||||
@@ -707,7 +709,10 @@ toBusinessChatInfo (Just chatType, Just businessId, Just customerId) = Just Busi
|
||||
toBusinessChatInfo _ = Nothing
|
||||
|
||||
groupInfoQuery :: Query
|
||||
groupInfoQuery =
|
||||
groupInfoQuery = groupInfoQueryFields <> " " <> groupInfoQueryFrom
|
||||
|
||||
groupInfoQueryFields :: Query
|
||||
groupInfoQueryFields =
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
@@ -716,13 +721,18 @@ groupInfoQuery =
|
||||
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,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
|
||||
|]
|
||||
|
||||
groupInfoQueryFrom :: Query
|
||||
groupInfoQueryFrom =
|
||||
[sql|
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
JOIN group_members mu ON mu.group_id = g.group_id
|
||||
@@ -808,7 +818,8 @@ setViaGroupLinkUri db groupId connId = do
|
||||
DB.query
|
||||
db
|
||||
"SELECT via_contact_uri, via_contact_uri_hash FROM connections WHERE connection_id = ?"
|
||||
(Only connId) :: IO [(Maybe ConnReqContact, Maybe ConnReqUriHash)]
|
||||
(Only connId) ::
|
||||
IO [(Maybe ConnReqContact, Maybe ConnReqUriHash)]
|
||||
forM_ (listToMaybe r) $ \(viaContactUri, viaContactUriHash) ->
|
||||
DB.execute
|
||||
db
|
||||
|
||||
@@ -487,6 +487,7 @@ data GroupInfo = GroupInfo
|
||||
chatItemTTL :: Maybe Int64,
|
||||
uiThemes :: Maybe UIThemeEntityOverrides,
|
||||
customData :: Maybe CustomData,
|
||||
groupSummary :: GroupSummary,
|
||||
membersRequireAttention :: Int,
|
||||
viaGroupLinkUri :: Maybe ConnReqContact
|
||||
}
|
||||
@@ -523,12 +524,9 @@ groupName' :: GroupInfo -> GroupName
|
||||
groupName' GroupInfo {localDisplayName = g} = g
|
||||
|
||||
data GroupSummary = GroupSummary
|
||||
{ currentMembers :: Int
|
||||
{ currentMembers :: Int64
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data GroupInfoSummary = GIS {groupInfo :: GroupInfo, groupSummary :: GroupSummary, groupLink :: Maybe GroupLink}
|
||||
deriving (Show)
|
||||
deriving (Eq, Show)
|
||||
|
||||
data GroupLink = GroupLink
|
||||
{ userContactLinkId :: Int64,
|
||||
@@ -1240,7 +1238,9 @@ memberPending m = case memberStatus m of
|
||||
memberCurrentOrPending :: GroupMember -> Bool
|
||||
memberCurrentOrPending m = memberCurrent m || memberPending m
|
||||
|
||||
-- update getGroupSummary if this is changed
|
||||
-- *** Please note:
|
||||
-- *** update getGroupSummary and SQL function used in update triggers if this is changed
|
||||
-- ***
|
||||
memberCurrent' :: GroupMemberStatus -> Bool
|
||||
memberCurrent' = \case
|
||||
GSMemRejected -> False
|
||||
@@ -2079,16 +2079,14 @@ $(JQ.deriveJSON defaultJSON ''BusinessChatInfo)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''PreparedGroup)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupSummary)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupInfo)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''Group)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupSummary)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupLink)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupInfoSummary)
|
||||
|
||||
instance FromField MsgFilter where fromField = fromIntField_ msgFilterIntP
|
||||
|
||||
instance ToField MsgFilter where toField = toField . msgFilterInt
|
||||
|
||||
@@ -128,7 +128,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRChatItemTTL u ttl -> ttyUser u $ viewChatItemTTL ttl
|
||||
CRNetworkConfig netCfg -> viewNetworkConfig netCfg
|
||||
CRContactInfo u ct cStats customUserProfile -> ttyUser u $ viewContactInfo ct cStats customUserProfile
|
||||
CRGroupInfo u g s -> ttyUser u $ viewGroupInfo g s
|
||||
CRGroupInfo u g -> ttyUser u $ viewGroupInfo g
|
||||
CRGroupMemberInfo u g m cStats -> ttyUser u $ viewGroupMemberInfo g m cStats
|
||||
CRQueueInfo _ msgInfo qInfo ->
|
||||
[ "last received msg: " <> maybe "none" viewJSON msgInfo,
|
||||
@@ -1360,13 +1360,13 @@ viewContactConnected ct userIncognitoProfile testView =
|
||||
Nothing ->
|
||||
[ttyFullContact ct <> ": contact is connected"]
|
||||
|
||||
viewGroupsList :: [GroupInfoSummary] -> [StyledString]
|
||||
viewGroupsList :: [GroupInfo] -> [StyledString]
|
||||
viewGroupsList [] = ["you have no groups!", "to create: " <> highlight' "/g <name>"]
|
||||
viewGroupsList gs = map groupSS $ sortOn ldn_ gs
|
||||
where
|
||||
ldn_ :: GroupInfoSummary -> Text
|
||||
ldn_ (GIS GroupInfo {localDisplayName} _ _) = T.toLower localDisplayName
|
||||
groupSS (GIS g@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}} GroupSummary {currentMembers} _) =
|
||||
ldn_ :: GroupInfo -> Text
|
||||
ldn_ GroupInfo {localDisplayName} = T.toLower localDisplayName
|
||||
groupSS g@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}, groupSummary = GroupSummary {currentMembers}} =
|
||||
case memberStatus membership of
|
||||
GSMemInvited -> groupInvitation' g
|
||||
s -> membershipIncognito g <> ttyFullGroup g <> viewMemberStatus s <> alias g
|
||||
@@ -1628,8 +1628,8 @@ viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, conta
|
||||
<> viewUITheme uiThemes
|
||||
<> viewCustomData customData
|
||||
|
||||
viewGroupInfo :: GroupInfo -> GroupSummary -> [StyledString]
|
||||
viewGroupInfo GroupInfo {groupId, uiThemes, customData} s =
|
||||
viewGroupInfo :: GroupInfo -> [StyledString]
|
||||
viewGroupInfo GroupInfo {groupId, uiThemes, customData, groupSummary = s} =
|
||||
[ "group ID: " <> sShow groupId,
|
||||
"current members: " <> sShow (currentMembers s)
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user