android, desktop: group knocking (#5816)

This commit is contained in:
spaced4ndy authored and GitHub committed 2025-04-21 15:17:21 +00:00
1 parent 3095b980cb
commit 805a69da94
34 files changed
+1844 -748

No files matched your search

+3 -3
View File
@@ -1578,7 +1578,7 @@ processChatCommand' vr = \case
case memberConnId m of
Just connId -> do
cStats@ConnectionStats {ratchetSyncState = rss} <- withAgent $ \a -> synchronizeRatchet a connId PQSupportOff force
(g', m', scopeInfo) <- liftIO $ mkGroupChatScope g m
(g', m', scopeInfo) <- mkGroupChatScope g m
createInternalChatItem user (CDGroupSnd g' scopeInfo) (CISndConnEvent . SCERatchetSync rss . Just $ groupMemberRef m') Nothing
pure $ CRGroupMemberRatchetSyncStarted user g' m' cStats
_ -> throwChatError CEGroupMemberNotActive
@@ -2244,7 +2244,7 @@ processChatCommand' vr = \case
pure m {memberStatus = GSMemRemoved}
deletePendingMember :: ([ChatError], [GroupMember], [AChatItem]) -> User -> GroupInfo -> [GroupMember] -> GroupMember -> CM ([ChatError], [GroupMember], [AChatItem])
deletePendingMember (accErrs, accDeleted, accACIs) user gInfo recipients m = do
(m', scopeInfo) <- liftIO $ mkMemberSupportChatInfo m
(m', scopeInfo) <- mkMemberSupportChatInfo m
(errs, deleted, acis) <- deleteMemsSend user gInfo (Just scopeInfo) recipients [m']
pure (errs <> accErrs, deleted <> accDeleted, acis <> accACIs)
deleteMemsSend :: User -> GroupInfo -> Maybe GroupChatScopeInfo -> [GroupMember] -> [GroupMember] -> CM ([ChatError], [GroupMember], [AChatItem])
@@ -2280,7 +2280,7 @@ processChatCommand' vr = \case
cancelFilesInProgress user filesInfo
let recipients = filter memberCurrentOrPending members
msg <- sendGroupMessage' user gInfo recipients XGrpLeave
(gInfo', scopeInfo) <- liftIO $ mkLocalGroupChatScope gInfo
(gInfo', scopeInfo) <- mkLocalGroupChatScope gInfo
ci <- saveSndChatItem user (CDGroupSnd gInfo' scopeInfo) msg (CISndGroupEvent SGEUserLeft)
toView $ CRNewChatItems user [AChatItem SCTGroup SMDSnd (GroupChat gInfo' scopeInfo) ci]
-- TODO delete direct connections that were unused
+14 -12
View File
@@ -1323,7 +1323,7 @@ getGroupRecipients vr user gInfo@GroupInfo {membership} scope modsCompatVersion
compatible GroupMember {activeConn, memberChatVRange} =
maxVersion (maybe memberChatVRange peerChatVRange activeConn) >= modsCompatVersion
mkLocalGroupChatScope :: GroupInfo -> IO (GroupInfo, Maybe GroupChatScopeInfo)
mkLocalGroupChatScope :: GroupInfo -> CM (GroupInfo, Maybe GroupChatScopeInfo)
mkLocalGroupChatScope gInfo@GroupInfo {membership}
| memberPending membership = do
(gInfo', scopeInfo) <- mkGroupSupportChatInfo gInfo
@@ -1331,7 +1331,7 @@ mkLocalGroupChatScope gInfo@GroupInfo {membership}
| otherwise =
pure (gInfo, Nothing)
mkGroupChatScope :: GroupInfo -> GroupMember -> IO (GroupInfo, GroupMember, Maybe GroupChatScopeInfo)
mkGroupChatScope :: GroupInfo -> GroupMember -> CM (GroupInfo, GroupMember, Maybe GroupChatScopeInfo)
mkGroupChatScope gInfo@GroupInfo {membership} m
| memberPending membership = do
(gInfo', scopeInfo) <- mkGroupSupportChatInfo gInfo
@@ -1344,38 +1344,40 @@ mkGroupChatScope gInfo@GroupInfo {membership} m
mkGetMessageChatScope :: VersionRangeChat -> User -> GroupInfo -> GroupMember -> Maybe MsgScope -> CM (GroupInfo, GroupMember, Maybe GroupChatScopeInfo)
mkGetMessageChatScope vr user gInfo@GroupInfo {membership} m msgScope_ =
liftIO (mkGroupChatScope gInfo m) >>= \case
mkGroupChatScope gInfo m >>= \case
groupScope@(_gInfo', _m', Just _scopeInfo) -> pure groupScope
(_, _, Nothing) -> case msgScope_ of
Nothing -> pure (gInfo, m, Nothing)
Just (MSMember mId)
| sameMemberId mId membership -> do
(gInfo', scopeInfo) <- liftIO $ mkGroupSupportChatInfo gInfo
(gInfo', scopeInfo) <- mkGroupSupportChatInfo gInfo
pure (gInfo', m, Just scopeInfo)
| otherwise -> do
referredMember <- withStore $ \db -> getGroupMemberByMemberId db vr user gInfo mId
-- TODO [knocking] return patched _referredMember' too?
(_referredMember', scopeInfo) <- liftIO $ mkMemberSupportChatInfo referredMember
(_referredMember', scopeInfo) <- mkMemberSupportChatInfo referredMember
pure (gInfo, m, Just scopeInfo)
mkGroupSupportChatInfo :: GroupInfo -> IO (GroupInfo, GroupChatScopeInfo)
mkGroupSupportChatInfo :: GroupInfo -> CM (GroupInfo, GroupChatScopeInfo)
mkGroupSupportChatInfo gInfo@GroupInfo {membership} =
case supportChat membership of
Nothing -> do
chatTs <- getCurrentTime
let gInfo' = gInfo {membership = membership {supportChat = Just $ GroupSupportChat chatTs 1 0 0}}
chatTs <- liftIO getCurrentTime
withStore' $ \db -> setSupportChatTs db (groupMemberId' membership) chatTs
let gInfo' = gInfo {membership = membership {supportChat = Just $ GroupSupportChat chatTs 0 0 0}}
scopeInfo = GCSIMemberSupport {groupMember_ = Nothing}
pure (gInfo', scopeInfo)
Just _supportChat ->
let scopeInfo = GCSIMemberSupport {groupMember_ = Nothing}
in pure (gInfo, scopeInfo)
mkMemberSupportChatInfo :: GroupMember -> IO (GroupMember, GroupChatScopeInfo)
mkMemberSupportChatInfo m@GroupMember {supportChat} =
mkMemberSupportChatInfo :: GroupMember -> CM (GroupMember, GroupChatScopeInfo)
mkMemberSupportChatInfo m@GroupMember {groupMemberId, supportChat} =
case supportChat of
Nothing -> do
chatTs <- getCurrentTime
let m' = m {supportChat = Just $ GroupSupportChat chatTs 1 0 0}
chatTs <- liftIO getCurrentTime
withStore' $ \db -> setSupportChatTs db groupMemberId chatTs
let m' = m {supportChat = Just $ GroupSupportChat chatTs 0 0 0}
scopeInfo = GCSIMemberSupport {groupMember_ = Just m'}
pure (m', scopeInfo)
Just _supportChat ->
+18 -18
View File
@@ -777,7 +777,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
else pure $ memberStatus membership
pure (GSMemConnected, membershipStatus)
toView $ CRUserJoinedGroup user gInfo {membership = membership {memberStatus = membershipStatus}} m {memberStatus = mStatus}
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
let cd = CDGroupRcv gInfo' scopeInfo m'
createInternalChatItem user cd (CIRcvGroupE2EEInfo E2EInfo {pqEnabled = PQEncOff}) Nothing
createGroupFeatureItems user cd CIRcvGroupFeature gInfo'
@@ -788,7 +788,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
if not (memberPending m)
then withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected
else pure $ memberStatus m
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
memberConnectedChatItem gInfo' scopeInfo m'
toView $ CRJoinedGroupMember user gInfo' m' {memberStatus = mStatus}
let Connection {viaUserContactLink} = conn
@@ -843,7 +843,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
tags <- newTVarIO []
withAckMessage "group msg" agentConnId msgMeta True (Just tags) $ \eInfo -> do
-- possible improvement is to choose scope based on event (some events specify scope)
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
checkIntegrityCreateItem (CDGroupRcv gInfo' scopeInfo m') msgMeta `catchChatError` \_ -> pure ()
forM_ aChatMsgs $ \case
Right (ACMsg _ chatMsg) ->
@@ -936,12 +936,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
when continued $ sendPendingGroupMessages user m conn
SWITCH qd phase cStats -> do
toView $ CRGroupMemberSwitch user gInfo m (SwitchProgress qd phase cStats)
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
when (phase == SPStarted || phase == SPCompleted) $ case qd of
QDRcv -> createInternalChatItem user (CDGroupSnd gInfo' scopeInfo) (CISndConnEvent . SCESwitchQueue phase . Just $ groupMemberRef m') Nothing
QDSnd -> createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m') (CIRcvConnEvent $ RCESwitchQueue phase) Nothing
RSYNC rss cryptoErr_ cStats -> do
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
case (rss, connectionCode, cryptoErr_) of
(RSRequired, _, Just cryptoErr) -> processErr gInfo' scopeInfo m' cryptoErr
(RSAllowed, _, Just cryptoErr) -> processErr gInfo' scopeInfo m' cryptoErr
@@ -1234,7 +1234,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| otherwise -> do
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo
mem <- acceptGroupJoinRequestAsync user gInfo cReq acceptance useRole profileMode
(gInfo', mem', scopeInfo) <- liftIO $ mkGroupChatScope gInfo mem
(gInfo', mem', scopeInfo) <- mkGroupChatScope gInfo mem
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo mem') (CIRcvGroupEvent RGEInvitedViaGroupLink) Nothing
toView $ CRAcceptingGroupJoinRequestMember user gInfo' mem'
Left rjctReason
@@ -1379,7 +1379,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
notifyMemberConnected :: GroupInfo -> GroupMember -> Maybe Contact -> CM ()
notifyMemberConnected gInfo m ct_ = do
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
memberConnectedChatItem gInfo' scopeInfo m'
lift $ mapM_ (`setContactNetworkStatus` NSConnected) ct_
toView $ CRConnectedToGroupMember user gInfo' m' ct_
@@ -2165,7 +2165,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
BCCustomer -> customerId == memberId
createProfileUpdatedItem m' =
when createItems $ do
(gInfo', m'', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m'
(gInfo', m'', scopeInfo) <- mkGroupChatScope gInfo m'
let ciContent = CIRcvGroupEvent $ RGEMemberProfileUpdated (fromLocalProfile p) p'
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m'') ciContent itemTs_
@@ -2458,7 +2458,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
Just (MSMember _) -> GSMemPendingReview
_ -> GSMemAnnounced
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} = do
(announcedMember', scopeInfo) <- liftIO $ getMemNewChatScope announcedMember
(announcedMember', scopeInfo) <- getMemNewChatScope announcedMember
let event = RGEMemberAdded groupMemberId (fromLocalProfile memberProfile)
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent event)
groupMsgToView gInfo scopeInfo ci
@@ -2555,7 +2555,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| senderRole < GRAdmin || senderRole < fromRole = messageError "x.grp.mem.role with insufficient member permissions"
| otherwise = do
withStore' $ \db -> updateGroupMemberRole db user member memRole
(gInfo'', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo' m
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView gInfo'' scopeInfo ci
toView CRMemberRole {user, groupInfo = gInfo'', byMember = m', member = member {memberRole = memRole}, fromRole, toRole = memRole}
@@ -2584,7 +2584,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
bm' <- setMemberBlocked bm
toggleNtf user bm' (not blocked)
let ciContent = CIRcvGroupEvent $ RGEMemberBlocked bmId (fromLocalProfile bmp) blocked
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs ciContent
groupMsgToView gInfo' scopeInfo ci
toView CRMemberBlockedForAll {user, groupInfo = gInfo', byMember = m', member = bm, blocked}
@@ -2669,7 +2669,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
messageError "x.grp.mem.del with insufficient member permissions"
| otherwise = a
deleteMemberItem gEvent = do
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView gInfo' scopeInfo ci
deleteMessages :: MsgDirectionI d => GroupMember -> SMsgDirection d -> CM ()
@@ -2682,7 +2682,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
deleteMemberConnection user m
-- member record is not deleted to allow creation of "member left" chat item
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemLeft
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
groupMsgToView gInfo' scopeInfo ci
toView $ CRLeftMember user gInfo' m' {memberStatus = GSMemLeft}
@@ -2696,7 +2696,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
pure members
-- member records are not deleted to keep history
deleteMembersConnections user ms
(gInfo'', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEGroupDeleted)
groupMsgToView gInfo'' scopeInfo ci
toView $ CRGroupDeleted user gInfo'' {membership = membership {memberStatus = GSMemGroupDeleted}} m'
@@ -2707,7 +2707,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| otherwise = case businessChat of
Nothing -> unless (p == p') $ do
g' <- withStore $ \db -> updateGroupProfile db user g p'
(g'', m', scopeInfo) <- liftIO $ mkGroupChatScope g' m
(g'', m', scopeInfo) <- mkGroupChatScope g' m
toView $ CRGroupUpdated user g g'' (Just m')
let cd = CDGroupRcv g'' scopeInfo m'
unless (sameGroupProfileInfo p p') $ do
@@ -2726,7 +2726,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
unless (groupPreferences p == Just ps') $ do
g' <- withStore' $ \db -> updateGroupPreferences db user g ps'
toView $ CRGroupUpdated user g g' (Just m)
(g'', m', scopeInfo) <- liftIO $ mkGroupChatScope g' m
(g'', m', scopeInfo) <- mkGroupChatScope g' m
let cd = CDGroupRcv g'' scopeInfo m'
createGroupFeatureChangedItems user cd CIRcvGroupFeature g g''
@@ -2768,7 +2768,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
dm <- encodeConnInfo $ XInfo p
joinAgentConnectionAsync user True connReq dm subMode
createItems mCt' m' = do
(g', m'', scopeInfo) <- liftIO $ mkGroupChatScope g m'
(g', m'', scopeInfo) <- mkGroupChatScope g m'
createInternalChatItem user (CDGroupRcv g' scopeInfo m'') (CIRcvGroupEvent RGEMemberCreatedContact) Nothing
toView $ CRNewMemberContactReceivedInv user mCt' g' m''
forM_ mContent_ $ \mc -> do
@@ -2827,7 +2827,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
groupMsgReceived :: GroupInfo -> GroupMember -> Connection -> MsgMeta -> NonEmpty MsgReceipt -> CM ()
groupMsgReceived gInfo m conn@Connection {connId} msgMeta msgRcpts = do
(gInfo', m', scopeInfo) <- liftIO $ mkGroupChatScope gInfo m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
checkIntegrityCreateItem (CDGroupRcv gInfo' scopeInfo m') msgMeta `catchChatError` \_ -> pure ()
forM_ msgRcpts $ \MsgReceipt {agentMsgId, msgRcptStatus} -> do
withStore' $ \db -> updateSndMsgDeliveryStatus db connId agentMsgId $ MDSSndRcvd msgRcptStatus
+9 -9
View File
@@ -321,7 +321,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
-- | creates completely new group with a single member - the current user
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
fullGroupPreferences = mergeGroupPreferences groupPreferences
currentTs <- getCurrentTime
customUserProfileId <- mapM (createIncognitoProfile_ db userId currentTs) incognitoProfile
@@ -329,8 +329,8 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
groupId <- liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -390,7 +390,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
DB.query db "SELECT group_id FROM groups WHERE inv_queue_info = ? AND user_id = ? LIMIT 1" (connRequest, userId)
createGroupInvitation_ :: ExceptT StoreError IO (GroupInfo, GroupMemberId)
createGroupInvitation_ = do
let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
fullGroupPreferences = mergeGroupPreferences groupPreferences
ExceptT $
withLocalDisplayName db userId displayName $ \localDisplayName -> runExceptT $ do
@@ -398,8 +398,8 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
groupId <- liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -558,13 +558,13 @@ createGroupViaLink'
(,) <$> getGroupInfo db vr user groupId <*> getGroupMemberById db vr user hostMemberId
where
insertGroup_ currentTs = ExceptT $ do
let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
withLocalDisplayName db userId displayName $ \localDisplayName -> runExceptT $ do
liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
+27 -14
View File
@@ -37,6 +37,7 @@ module Simplex.Chat.Store.Messages
deleteOldMessages,
MemberAttention (..),
updateChatTsStats,
setSupportChatTs,
createNewSndChatItem,
createNewRcvChatItem,
createNewChatItemNoMsg,
@@ -419,6 +420,10 @@ updateChatTsStats db User {userId} chatDirection chatTs chatStats_ = case toChat
(chatTs, userId, noteFolderId)
_ -> pure ()
setSupportChatTs :: DB.Connection -> GroupMemberId -> UTCTime -> IO ()
setSupportChatTs db groupMemberId chatTs =
DB.execute db "UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?" (chatTs, groupMemberId)
createNewSndChatItem :: DB.Connection -> User -> ChatDirection c 'MDSnd -> SndMessage -> CIContent 'MDSnd -> Maybe (CIQuote c) -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> UTCTime -> IO ChatItemId
createNewSndChatItem db user chatDirection SndMessage {msgId, sharedMsgId} ciContent quotedItem itemForwarded timed live createdAt =
createNewChatItem_ db user chatDirection createdByMsgId (Just sharedMsgId) ciContent quoteRow itemForwarded timed live False createdAt Nothing createdAt
@@ -750,7 +755,7 @@ findGroupChatPreviews_ db User {userId} pagination clq =
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -1812,14 +1817,22 @@ getLocalNavInfo_ db User {userId} NoteFolder {noteFolderId} afterCI = do
:. (userId, noteFolderId, ciCreatedAt afterCI, cChatItemId afterCI)
)
toChatItemRef :: (ChatItemId, Maybe Int64, Maybe Int64, Maybe Int64) -> Either StoreError (ChatRef, ChatItemId)
toChatItemRef ::
(ChatItemId, Maybe ContactId, Maybe GroupId, Maybe GroupChatScopeTag, Maybe GroupMemberId, Maybe NoteFolderId) ->
Either StoreError (ChatRef, ChatItemId)
toChatItemRef = \case
(itemId, Just contactId, Nothing, Nothing) -> Right (ChatRef CTDirect contactId Nothing, itemId)
-- For "chat item refs" we don't care about scope, as they are only used for getting item by id (via getAChatItem).
-- To get the scope without joining group_members, we could additionally store group scope "tag" to differentiate member support chats.
(itemId, Nothing, Just groupId, Nothing) -> Right (ChatRef CTGroup groupId Nothing, itemId)
(itemId, Nothing, Nothing, Just folderId) -> Right (ChatRef CTLocal folderId Nothing, itemId)
(itemId, _, _, _) -> Left $ SEBadChatItem itemId Nothing
(itemId, Just contactId, Nothing, Nothing, Nothing, Nothing) ->
Right (ChatRef CTDirect contactId Nothing, itemId)
(itemId, Nothing, Just groupId, Nothing, Nothing, Nothing) ->
Right (ChatRef CTGroup groupId Nothing, itemId)
(itemId, Nothing, Just groupId, Just GCSTMemberSupport_, Nothing, Nothing) ->
Right (ChatRef CTGroup groupId (Just (GCSMemberSupport Nothing)), itemId)
(itemId, Nothing, Just groupId, Just GCSTMemberSupport_, Just scopeGMId, Nothing) ->
Right (ChatRef CTGroup groupId (Just (GCSMemberSupport $ Just scopeGMId)), itemId)
(itemId, Nothing, Nothing, Nothing, Nothing, Just folderId) ->
Right (ChatRef CTLocal folderId Nothing, itemId)
(itemId, _, _, _, _, _) ->
Left $ SEBadChatItem itemId Nothing
updateDirectChatItemsRead :: DB.Connection -> User -> ContactId -> IO ()
updateDirectChatItemsRead db User {userId} contactId = do
@@ -2170,7 +2183,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
<$> DB.query
db
[sql|
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
ORDER BY item_ts DESC, chat_item_id DESC
@@ -2181,7 +2194,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
DB.query
db
[sql|
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
AND (item_ts > ? OR (item_ts = ? AND chat_item_id > ?))
@@ -2194,7 +2207,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
<$> DB.query
db
[sql|
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
AND (item_ts < ? OR (item_ts = ? AND chat_item_id < ?))
@@ -2206,7 +2219,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
DB.query
db
[sql|
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE chat_item_id = ?
|]
@@ -2952,7 +2965,7 @@ getChatItemByFileId db vr user@User {userId} fileId = do
DB.query
db
[sql|
SELECT i.chat_item_id, i.contact_id, i.group_id, i.note_folder_id
SELECT i.chat_item_id, i.contact_id, i.group_id, i.group_scope_tag, i.group_scope_group_member_id, i.note_folder_id
FROM chat_items i
JOIN files f ON f.chat_item_id = i.chat_item_id
WHERE f.user_id = ? AND f.file_id = ?
@@ -2974,7 +2987,7 @@ getChatItemByGroupId db vr user@User {userId} groupId = do
DB.query
db
[sql|
SELECT i.chat_item_id, i.contact_id, i.group_id, i.note_folder_id
SELECT i.chat_item_id, i.contact_id, i.group_id, i.group_scope_tag, i.group_scope_group_member_id, i.note_folder_id
FROM chat_items i
JOIN groups g ON g.chat_item_id = i.chat_item_id
WHERE g.user_id = ? AND g.group_id = ?
@@ -462,7 +462,7 @@ SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
Query:
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
AND (item_ts < ? OR (item_ts = ? AND chat_item_id < ?))
@@ -474,7 +474,7 @@ SEARCH chat_items USING INDEX idx_chat_items_group_scope_item_ts (user_id=?)
USE TEMP B-TREE FOR ORDER BY
Query:
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
ORDER BY item_ts DESC, chat_item_id DESC
@@ -578,7 +578,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
SEARCH i USING COVERING INDEX idx_chat_items_group_shared_msg_id (user_id=? AND group_id=? AND group_member_id=? AND shared_msg_id=?) LEFT-JOIN
Query:
SELECT i.chat_item_id, i.contact_id, i.group_id, i.note_folder_id
SELECT i.chat_item_id, i.contact_id, i.group_id, i.group_scope_tag, i.group_scope_group_member_id, i.note_folder_id
FROM chat_items i
JOIN files f ON f.chat_item_id = i.chat_item_id
WHERE f.user_id = ? AND f.file_id = ?
@@ -1014,7 +1014,7 @@ Plan:
SEARCH chat_items USING INDEX idx_chat_items_group_id (group_id=?)
Query:
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE chat_item_id = ?
@@ -1022,7 +1022,7 @@ Plan:
SEARCH chat_items USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT chat_item_id, contact_id, group_id, note_folder_id
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
AND (item_ts > ? OR (item_ts = ? AND chat_item_id > ?))
@@ -1179,7 +1179,7 @@ SEARCH i USING INDEX idx_chat_items_group_id (group_id=?)
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT i.chat_item_id, i.contact_id, i.group_id, i.note_folder_id
SELECT i.chat_item_id, i.contact_id, i.group_id, i.group_scope_tag, i.group_scope_group_member_id, i.note_folder_id
FROM chat_items i
JOIN groups g ON g.chat_item_id = i.chat_item_id
WHERE g.user_id = ? AND g.group_id = ?
@@ -1952,7 +1952,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -1995,7 +1995,7 @@ SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2004,7 +2004,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2041,7 +2041,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2050,7 +2050,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2086,7 +2086,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2095,7 +2095,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2131,7 +2131,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2140,7 +2140,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2176,7 +2176,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2185,7 +2185,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2221,7 +2221,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2230,7 +2230,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2266,7 +2266,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2275,7 +2275,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2311,7 +2311,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2320,7 +2320,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2353,7 +2353,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2362,7 +2362,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2395,7 +2395,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -2404,7 +2404,7 @@ Query:
(
SELECT chat_item_id
FROM chat_items ci
WHERE ci.user_id = ? AND ci.group_id = g.group_id
WHERE ci.user_id = ? AND ci.group_id = g.group_id AND ci.group_scope_tag IS NULL
ORDER BY ci.item_ts DESC
LIMIT 1
) AS chat_item_id,
@@ -2437,7 +2437,7 @@ SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING COVERING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
Query:
SELECT
@@ -5460,7 +5460,7 @@ Plan:
Query: INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)
Query: INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?)
+1 -1
View File
@@ -1858,7 +1858,7 @@ $(JQ.deriveJSON defaultJSON ''LocalProfile)
$(JQ.deriveJSON defaultJSON ''UserContactRequest)
$(JQ.deriveJSON (enumJSON $ dropPrefix "MC") ''MemberCriteria)
$(JQ.deriveJSON (enumJSON $ dropPrefix "MC") {J.tagSingleConstructors = True} ''MemberCriteria)
$(JQ.deriveJSON defaultJSON ''GroupMemberAdmission)