core: member requires attention counter (#5881)

This commit is contained in:
spaced4ndy authored and GitHub committed 2025-05-09 15:36:06 +00:00
1 parent 2a9df3e10b
commit 79991e84c9
33 files changed
+530 -494

No files matched your search

+1
View File
@@ -676,6 +676,7 @@ data ChatResponse
| CRSentConfirmation {user :: User, connection :: PendingContactConnection}
| CRSentInvitation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
| CRSentInvitationToContact {user :: User, contact :: Contact, customUserProfile :: Maybe Profile}
| CRItemsReadForChat {user :: User, chatInfo :: AChatInfo}
| CRContactDeleted {user :: User, contact :: Contact}
| CRChatCleared {user :: User, chatInfo :: AChatInfo}
| CRUserContactLinkCreated {user :: User, connLinkContact :: CreatedLinkContact}
+20 -10
View File
@@ -1017,22 +1017,26 @@ processChatCommand' vr = \case
CTContactConnection -> throwCmdError "not supported"
APIChatItemsRead chatRef@(ChatRef cType chatId scope) itemIds -> withUser $ \_ -> case cType of
CTDirect -> do
user <- withFastStore $ \db -> getUserByContactId db chatId
(user, ct) <- withFastStore $ \db -> do
user <- getUserByContactId db chatId
ct <- getContact db vr user chatId
pure (user, ct)
timedItems <- withFastStore' $ \db -> do
timedItems <- updateDirectChatItemsReadList db user chatId itemIds
setDirectChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
forM_ timedItems $ \(itemId, deleteAt) -> startProximateTimedItemThread user (chatRef, itemId) deleteAt
ok user
pure $ CRItemsReadForChat user (AChatInfo SCTDirect $ DirectChat ct)
CTGroup -> do
(user, gInfo) <- withFastStore $ \db -> do
user <- getUserByGroupId db chatId
gInfo <- getGroupInfo db vr user chatId
pure (user, gInfo)
timedItems <- withFastStore' $ \db -> do
timedItems <- updateGroupChatItemsReadList db user gInfo scope itemIds
setGroupChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
(timedItems, gInfo') <- withFastStore $ \db -> do
(timedItems, gInfo') <- updateGroupChatItemsReadList db vr user gInfo scope itemIds
timedItems' <- liftIO $ setGroupChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
pure (timedItems', gInfo')
forM_ timedItems $ \(itemId, deleteAt) -> startProximateTimedItemThread user (chatRef, itemId) deleteAt
ok user
pure $ CRItemsReadForChat user (AChatInfo SCTGroup $ GroupChat gInfo' Nothing)
CTLocal -> throwCmdError "not supported"
CTContactRequest -> throwCmdError "not supported"
CTContactConnection -> throwCmdError "not supported"
@@ -2054,8 +2058,11 @@ processChatCommand' vr = \case
void $ sendDirectMemberMessage mConn msg groupId
introduceToRemaining vr user gInfo m {memberRole = role}
when (groupFeatureAllowed SGFHistory gInfo) $ sendHistory user gInfo m
m' <- withFastStore' $ \db -> updateGroupMemberAccepted db user m GSMemConnected role
pure $ CRMemberAccepted user gInfo m'
(m', gInfo') <- withFastStore' $ \db -> do
m' <- updateGroupMemberAccepted db user m GSMemConnected role
gInfo' <- updateGroupMembersRequireAttention db user gInfo m m'
pure (m', gInfo')
pure $ CRMemberAccepted user gInfo' m'
Nothing -> throwChatError CEGroupMemberNotActive
GSMemPendingReview -> do
let scope = Just $ GCSMemberSupport $ Just (groupMemberId' m)
@@ -2063,8 +2070,11 @@ processChatCommand' vr = \case
let rcpModMs' = filter memberCurrent modMs
msg = XGrpLinkAcpt GAAccepted role (memberId' m)
void $ sendGroupMessage user gInfo scope ([m] <> rcpModMs') msg
m' <- withFastStore' $ \db -> updateGroupMemberAccepted db user m newMemberStatus role
pure $ CRMemberAccepted user gInfo m'
(m', gInfo') <- withFastStore' $ \db -> do
m' <- updateGroupMemberAccepted db user m newMemberStatus role
gInfo' <- updateGroupMembersRequireAttention db user gInfo m m'
pure (m', gInfo')
pure $ CRMemberAccepted user gInfo' m'
where
newMemberStatus = case memberConn m of
Just c | connReady c -> GSMemConnected
+18 -11
View File
@@ -2032,8 +2032,9 @@ saveSndChatItems ::
CM [Either ChatError (ChatItem c 'MDSnd)]
saveSndChatItems user cd itemsData itemTimed live = do
createdAt <- liftIO getCurrentTime
vr <- chatVersionRange
when (contactChatDeleted cd || any (\NewSndChatItemData {content} -> ciRequiresAttention content) (rights itemsData)) $
withStore' (\db -> updateChatTsStats db user cd createdAt Nothing)
void $ withStore' (\db -> updateChatTsStats db vr user cd createdAt Nothing)
lift $ withStoreBatch (\db -> map (bindRight $ createItem db createdAt) itemsData)
where
createItem :: DB.Connection -> UTCTime -> NewSndChatItemData c -> IO (Either ChatError (ChatItem c 'MDSnd))
@@ -2045,19 +2046,20 @@ saveSndChatItems user cd itemsData itemTimed live = do
CDGroupSnd g _scope | not (null itemMentions) -> createGroupCIMentions db g ci itemMentions
_ -> pure ci
saveRcvChatItemNoParse :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> CIContent 'MDRcv -> CM (ChatItem c 'MDRcv)
saveRcvChatItemNoParse :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> CIContent 'MDRcv -> CM (ChatItem c 'MDRcv, ChatInfo c)
saveRcvChatItemNoParse user cd msg brokerTs = saveRcvChatItem user cd msg brokerTs . ciContentNoParse
saveRcvChatItem :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> CM (ChatItem c 'MDRcv)
saveRcvChatItem :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> CM (ChatItem c 'MDRcv, ChatInfo c)
saveRcvChatItem user cd msg@RcvMessage {sharedMsgId_} brokerTs content =
saveRcvChatItem' user cd msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
ciContentNoParse :: CIContent 'MDRcv -> (CIContent 'MDRcv, (Text, Maybe MarkdownList))
ciContentNoParse content = (content, (ciContentToText content, Nothing))
saveRcvChatItem' :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> Maybe (CIFile 'MDRcv) -> Maybe CITimed -> Bool -> Map MemberName MsgMention -> CM (ChatItem c 'MDRcv)
saveRcvChatItem' :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> Maybe (CIFile 'MDRcv) -> Maybe CITimed -> Bool -> Map MemberName MsgMention -> CM (ChatItem c 'MDRcv, ChatInfo c)
saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} sharedMsgId_ brokerTs (content, (t, ft_)) ciFile itemTimed live mentions = do
createdAt <- liftIO getCurrentTime
vr <- chatVersionRange
withStore' $ \db -> do
(mentions' :: Map MemberName CIMention, userMention) <- case cd of
CDGroupRcv g@GroupInfo {membership} _scope _m -> do
@@ -2068,13 +2070,16 @@ saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} shared
userMention' = userReply || any (\CIMention {memberId} -> sameMemberId memberId membership) mentions'
in pure (mentions', userMention')
CDDirectRcv _ -> pure (M.empty, False)
when (ciRequiresAttention content || contactChatDeleted cd) $ updateChatTsStats db user cd createdAt (chatStatsCounts userMention)
cInfo' <- if (ciRequiresAttention content || contactChatDeleted cd)
then updateChatTsStats db vr user cd createdAt (chatStatsCounts userMention)
else pure $ toChatInfo cd
(ciId, quotedItem, itemForwarded) <- createNewRcvChatItem db user cd msg sharedMsgId_ content itemTimed live userMention brokerTs createdAt
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
let ci = mkChatItem_ cd ciId content (t, ft_) ciFile quotedItem sharedMsgId_ itemForwarded itemTimed live userMention brokerTs forwardedByMember createdAt
case cd of
ci' <- case cd of
CDGroupRcv g _scope _m | not (null mentions') -> createGroupCIMentions db g ci mentions'
_ -> pure ci
pure (ci', cInfo')
where
chatStatsCounts :: Bool -> Maybe (Int, MemberAttention, Int)
chatStatsCounts userMention = case cd of
@@ -2297,12 +2302,13 @@ createInternalItemsForChats ::
createInternalItemsForChats user itemTs_ dirsCIContents = do
createdAt <- liftIO getCurrentTime
let itemTs = fromMaybe createdAt itemTs_
void . withStoreBatch' $ \db -> map (uncurry $ updateChat db createdAt) dirsCIContents
vr <- chatVersionRange'
void . withStoreBatch' $ \db -> map (uncurry $ updateChat db vr createdAt) dirsCIContents
withStoreBatch' $ \db -> concatMap (uncurry $ createACIs db itemTs createdAt) dirsCIContents
where
updateChat :: DB.Connection -> UTCTime -> ChatDirection c d -> [CIContent d] -> IO ()
updateChat db createdAt cd contents
| any ciRequiresAttention contents || contactChatDeleted cd = updateChatTsStats db user cd createdAt chatStatsCounts
updateChat :: DB.Connection -> VersionRangeChat -> UTCTime -> ChatDirection c d -> [CIContent d] -> IO ()
updateChat db vr createdAt cd contents
| any ciRequiresAttention contents || contactChatDeleted cd = void $ updateChatTsStats db vr user cd createdAt chatStatsCounts
| otherwise = pure ()
where
chatStatsCounts :: Maybe (Int, MemberAttention, Int)
@@ -2331,7 +2337,8 @@ createLocalChatItems ::
UTCTime ->
CM [ChatItem 'CTLocal 'MDSnd]
createLocalChatItems user cd itemsData createdAt = do
withStore' $ \db -> updateChatTsStats db user cd createdAt Nothing
vr <- chatVersionRange
void $ withStore' $ \db -> updateChatTsStats db vr user cd createdAt Nothing
(errs, items) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (createItem db) $ L.toList itemsData)
unless (null errs) $ toView $ CEvtChatErrors errs
pure items
+90 -74
View File
@@ -784,29 +784,33 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
memberConnectedChatItem gInfo' scopeInfo m'
unless (memberPending membership) $ maybeCreateGroupDescrLocal gInfo' m'
GCInviteeMember -> do
mStatus <-
(gInfo', mStatus) <-
if not (memberPending m)
then withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected
else pure $ memberStatus m
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
memberConnectedChatItem gInfo' scopeInfo m'
then do
mStatus <- withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected
pure (gInfo, mStatus)
else do
gInfo' <- withStore' $ \db -> increaseGroupMembersRequireAttention db user gInfo
pure (gInfo', memberStatus m)
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
memberConnectedChatItem gInfo'' scopeInfo m'
case scopeInfo of
Just (GCSIMemberSupport _) -> do
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing
createInternalChatItem user (CDGroupRcv gInfo'' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing
_ -> pure ()
toView $ CEvtJoinedGroupMember user gInfo' m' {memberStatus = mStatus}
toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus}
let Connection {viaUserContactLink} = conn
when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo'
when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo''
when (connChatVersion < batchSend2Version) sendGroupAutoReply
case mStatus of
GSMemPendingApproval -> pure ()
GSMemPendingReview -> introduceToModerators vr user gInfo' m'
GSMemPendingReview -> introduceToModerators vr user gInfo'' m'
_ -> do
introduceToAll vr user gInfo' m'
when (groupFeatureAllowed SGFHistory gInfo') $ sendHistory user gInfo' m'
introduceToAll vr user gInfo'' m'
when (groupFeatureAllowed SGFHistory gInfo'') $ sendHistory user gInfo'' m'
where
sendXGrpLinkMem gInfo' = do
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo'
sendXGrpLinkMem gInfo'' = do
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo''
profileToSend = profileToSendOnAccept user profileMode True
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId
_ -> do
@@ -1466,9 +1470,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
where
brokerTs = metaBrokerTs msgMeta
newChatItem content ciFile_ timed_ live = do
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile_ timed_ live M.empty
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile_ timed_ live M.empty
reactions <- maybe (pure []) (\sharedMsgId -> withStore' $ \db -> getDirectCIReactions db ct sharedMsgId) sharedMsgId_
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci {reactions}]
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci {reactions}]
autoAcceptFile :: Maybe (RcvFileTransfer, CIFile 'MDRcv) -> CM ()
autoAcceptFile = mapM_ $ \(ft, CIFile {fileSize}) -> do
@@ -1534,11 +1538,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- Chat item and update message which created it will have different sharedMsgId in this case...
let timed_ = rcvContactCITimed ct ttl
ts = ciContentTexts content
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live M.empty
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live M.empty
ci' <- withStore' $ \db -> do
createChatItemVersion db (chatItemId' ci) brokerTs mc
updateDirectChatItem' db user contactId ci content True live Nothing Nothing
toView $ CEvtChatItemUpdated user (AChatItem SCTDirect SMDRcv (DirectChat ct) ci')
toView $ CEvtChatItemUpdated user (AChatItem SCTDirect SMDRcv cInfo ci')
where
brokerTs = metaBrokerTs msgMeta
content = CIRcvMsgContent mc
@@ -1658,27 +1662,26 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| groupFeatureAllowed SGFFullDelete gInfo = do
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
-- ignores member role when blocked by admin
ci <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvBlocked) Nothing timed' False M.empty
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvBlocked) Nothing timed' False M.empty
ci' <- withStore' $ \db -> updateGroupCIBlockedByAdmin db user gInfo' ci brokerTs
groupMsgToView gInfo' scopeInfo ci'
groupMsgToView cInfo ci'
| otherwise = do
file_ <- processFileInv
ci <- createNonLive file_
(ci, cInfo) <- createNonLive file_
ci' <- withStore' $ \db -> markGroupCIBlockedByAdmin db user gInfo ci
(gInfo', _m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
groupMsgToView gInfo' scopeInfo ci'
groupMsgToView cInfo ci'
applyModeration CIModeration {moderatorMember = moderator@GroupMember {memberRole = moderatorRole}, moderatedAt}
| moderatorRole < GRModerator || moderatorRole < memberRole =
createContentItem
| groupFeatureMemberAllowed SGFFullDelete moderator gInfo = do
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
ci <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvModerated) Nothing timed' False M.empty
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvModerated) Nothing timed' False M.empty
ci' <- withStore' $ \db -> updateGroupChatItemModerated db user gInfo' ci moderator moderatedAt
groupMsgToView gInfo' scopeInfo ci'
groupMsgToView cInfo ci'
| otherwise = do
(gInfo', _m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
file_ <- processFileInv
ci <- createNonLive file_
(ci, _cInfo) <- createNonLive file_
deletions <- markGroupCIsDeleted user gInfo' scopeInfo [CChatItem SMDRcv ci] (Just moderator) moderatedAt
toView $ CEvtChatItemsDeleted user deletions False False
createNonLive file_ = do
@@ -1693,10 +1696,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
newChatItem ciContent ciFile_ timed_ live = do
let mentions' = if showMessages (memberSettings m) then mentions else []
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
ci <- saveRcvCI gInfo' scopeInfo m' ciContent ciFile_ timed_ live mentions'
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' ciContent ciFile_ timed_ live mentions'
ci' <- blockedMember m' ci $ withStore' $ \db -> markGroupChatItemBlocked db user gInfo' ci
reactions <- maybe (pure []) (\sharedMsgId -> withStore' $ \db -> getGroupCIReactions db gInfo' memberId sharedMsgId) sharedMsgId_
groupMsgToView gInfo' scopeInfo ci' {reactions}
groupMsgToView cInfo ci' {reactions}
groupMessageUpdate :: GroupInfo -> GroupMember -> SharedMsgId -> MsgContent -> Map MemberName MsgMention -> Maybe MsgScope -> RcvMessage -> UTCTime -> Maybe Int -> Maybe Bool -> CM ()
groupMessageUpdate gInfo@GroupInfo {groupId} m@GroupMember {groupMemberId, memberId} sharedMsgId mc mentions msgScope_ msg@RcvMessage {msgId} brokerTs ttl_ live_
@@ -1710,12 +1713,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
let timed_ = rcvGroupCITimed gInfo ttl_
mentions' = if showMessages (memberSettings m) then mentions else []
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
ci <- saveRcvChatItem' user (CDGroupRcv gInfo' scopeInfo m') msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live mentions'
(ci, cInfo) <- saveRcvChatItem' user (CDGroupRcv gInfo' scopeInfo m') msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live mentions'
ci' <- withStore' $ \db -> do
createChatItemVersion db (chatItemId' ci) brokerTs mc
ci' <- updateGroupChatItem db user groupId ci content True live Nothing
blockedMember m' ci' $ markGroupChatItemBlocked db user gInfo' ci'
toView $ CEvtChatItemUpdated user (AChatItem SCTGroup SMDRcv (GroupChat gInfo' scopeInfo) ci')
toView $ CEvtChatItemUpdated user (AChatItem SCTGroup SMDRcv cInfo ci')
where
content = CIRcvMsgContent mc
ts@(_, ft_) = msgContentTexts mc
@@ -1802,8 +1805,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
ciFile = Just $ CIFile {fileId, fileName, fileSize, fileSource = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol}
content = ciContentNoParse $ CIRcvMsgContent $ MCFile ""
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
where
brokerTs = metaBrokerTs msgMeta
@@ -1816,9 +1819,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
ciFile = Just $ CIFile {fileId, fileName, fileSize, fileSource = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol}
content = ciContentNoParse $ CIRcvMsgContent $ MCFile ""
ci <- saveRcvChatItem' user (CDGroupRcv gInfo Nothing m) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
(ci, cInfo) <- saveRcvChatItem' user (CDGroupRcv gInfo Nothing m) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
ci' <- blockedMember m ci $ withStore' $ \db -> markGroupChatItemBlocked db user gInfo ci
groupMsgToView gInfo Nothing ci'
groupMsgToView cInfo ci'
blockedMember :: Monad m' => GroupMember -> ChatItem c d -> m' (ChatItem c d) -> m' (ChatItem c d)
blockedMember m ci blockedCI
@@ -1969,9 +1972,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
_ -> messageError "x.file.acpt.inv: member connection is not active"
else messageError "x.file.acpt.inv: fileName is different from expected"
groupMsgToView :: forall d. MsgDirectionI d => GroupInfo -> Maybe GroupChatScopeInfo -> ChatItem 'CTGroup d -> CM ()
groupMsgToView gInfo chatScopeInfo ci = do
toView $ CEvtNewChatItems user [AChatItem SCTGroup (msgDirection @d) (GroupChat gInfo chatScopeInfo) ci]
groupMsgToView :: forall d. MsgDirectionI d => ChatInfo 'CTGroup -> ChatItem 'CTGroup d -> CM ()
groupMsgToView cInfo ci = do
toView $ CEvtNewChatItems user [AChatItem SCTGroup (msgDirection @d) cInfo ci]
processGroupInvitation :: Contact -> GroupInvitation -> RcvMessage -> MsgMeta -> CM ()
processGroupInvitation ct inv msg msgMeta = do
@@ -1996,9 +1999,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
toView $ CEvtUserAcceptedGroupSent user gInfo {membership = membership {memberStatus = GSMemAccepted}} (Just ct)
else do
let content = CIRcvGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending}) memRole
ci <- saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs content
(ci, cInfo) <- saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs content
withStore' $ \db -> setGroupInvitationChatItemId db user groupId (chatItemId' ci)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
toView $ CEvtReceivedGroupInvitation {user, groupInfo = gInfo, contact = ct, fromMemberRole = fromRole, memberRole = memRole}
where
brokerTs = metaBrokerTs msgMeta
@@ -2024,8 +2027,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
forM_ contactConns $ \conn -> withStore' $ \db -> updateConnectionStatus db conn ConnDeleted
activeConn' <- forM (contactConn ct') $ \conn -> pure conn {connStatus = ConnDeleted}
let ct'' = ct' {activeConn = activeConn'} :: Contact
ci <- saveRcvChatItemNoParse user (CDDirectRcv ct'') msg brokerTs (CIRcvDirectEvent RDEContactDeleted)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct'') ci]
(ci, cInfo) <- saveRcvChatItemNoParse user (CDDirectRcv ct'') msg brokerTs (CIRcvDirectEvent RDEContactDeleted)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
toView $ CEvtContactDeletedByContact user ct''
else do
contactConns <- withStore' $ \db -> getContactConnections db vr userId c
@@ -2096,13 +2099,16 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memberId) >>= \case
Left _ -> messageError "x.grp.link.acpt error: referenced member does not exist"
Right referencedMember -> do
referencedMember' <- withFastStore' $ \db -> updateGroupMemberAccepted db user referencedMember (newMemberStatus referencedMember) role
(referencedMember', gInfo') <- withFastStore' $ \db -> do
referencedMember' <- updateGroupMemberAccepted db user referencedMember (newMemberStatus referencedMember) role
gInfo' <- updateGroupMembersRequireAttention db user gInfo referencedMember referencedMember'
pure (referencedMember', gInfo')
when (memberCategory referencedMember == GCInviteeMember) $ introduceToRemainingMembers referencedMember'
let scopeInfo = Just $ GCSIMemberSupport {groupMember_ = Just referencedMember'}
gEvent = RGEMemberAccepted (groupMemberId' referencedMember') (fromLocalProfile $ memberProfile referencedMember')
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView gInfo scopeInfo ci
toView $ CEvtMemberAcceptedByOther user gInfo m referencedMember'
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m) msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView cInfo ci
toView $ CEvtMemberAcceptedByOther user gInfo' m referencedMember'
where
newMemberStatus refMem = case memberConn refMem of
Just c | connReady c -> GSMemConnected
@@ -2112,8 +2118,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
GAAccepted -> do
membership' <- withStore' $ \db -> updateGroupMemberAccepted db user membership GSMemConnected role
let scopeInfo = Just $ GCSIMemberSupport {groupMember_ = Nothing}
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent RGEUserAccepted)
groupMsgToView gInfo scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent RGEUserAccepted)
groupMsgToView cInfo ci
toView $ CEvtUserJoinedGroup user gInfo {membership = membership'} m
let cd = CDGroupRcv gInfo Nothing m
createInternalChatItem user cd (CIRcvGroupE2EEInfo E2EInfo {pqEnabled = PQEncOff}) Nothing
@@ -2265,7 +2271,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
then do
g <- asks random
dhKeyPair <- atomically $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
ci <- saveCallItem CISCallPending
(ci, cInfo) <- saveCallItem CISCallPending
callUUID <- UUID.toText <$> liftIO V4.nextRandom
let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> (snd <$> dhKeyPair))
callState = CallInvitationReceived {peerCallType = callType, localDhPubKey = fst <$> dhKeyPair, sharedKey}
@@ -2278,15 +2284,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
call_ <- atomically (TM.lookupInsert contactId call' calls)
forM_ call_ $ \call -> updateCallItemStatus user ct call WCSDisconnected Nothing
toView $ CEvtCallInvitation RcvCallInvitation {user, contact = ct, callType, sharedKey, callUUID, callTs = chatItemTs' ci}
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
else featureRejected CFCalls
where
brokerTs = metaBrokerTs msgMeta
saveCallItem status = saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs (CIRcvCall status 0)
featureRejected f = do
let content = ciContentNoParse $ CIRcvChatFeatureRejected f
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
-- to party initiating call
xCallOffer :: Contact -> CallId -> CallOffer -> RcvMessage -> CM ()
@@ -2457,27 +2463,37 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
unless (sameMemberId memId $ membership gInfo) $
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memId) >>= \case
Right unknownMember@GroupMember {memberStatus = GSMemUnknown} -> do
updatedMember <- withStore $ \db -> updateUnknownMemberAnnounced db vr user m unknownMember memInfo initialStatus
toView $ CEvtUnknownMemberAnnounced user gInfo m unknownMember updatedMember
memberAnnouncedToView updatedMember
(updatedMember, gInfo') <- withStore $ \db -> do
updatedMember <- updateUnknownMemberAnnounced db vr user m unknownMember memInfo initialStatus
gInfo' <- if memberPending updatedMember
then liftIO $ increaseGroupMembersRequireAttention db user gInfo
else pure gInfo
pure (updatedMember, gInfo')
toView $ CEvtUnknownMemberAnnounced user gInfo' m unknownMember updatedMember
memberAnnouncedToView updatedMember gInfo'
Right _ -> messageError "x.grp.mem.new error: member already exists"
Left _ -> do
newMember <- withStore $ \db -> createNewGroupMember db user gInfo m memInfo GCPostMember initialStatus
memberAnnouncedToView newMember
(newMember, gInfo') <- withStore $ \db -> do
newMember <- createNewGroupMember db user gInfo m memInfo GCPostMember initialStatus
gInfo' <- if memberPending newMember
then liftIO $ increaseGroupMembersRequireAttention db user gInfo
else pure gInfo
pure (newMember, gInfo')
memberAnnouncedToView newMember gInfo'
where
initialStatus = case msgScope_ of
Just (MSMember _) -> GSMemPendingReview
_ -> GSMemAnnounced
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} = do
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} gInfo' = do
(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
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m) msg brokerTs (CIRcvGroupEvent event)
groupMsgToView cInfo ci
case scopeInfo of
Just (GCSIMemberSupport _) -> do
createInternalChatItem user (CDGroupRcv gInfo scopeInfo m) (CIRcvGroupEvent RGENewMemberPendingReview) (Just brokerTs)
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m) (CIRcvGroupEvent RGENewMemberPendingReview) (Just brokerTs)
_ -> pure ()
toView $ CEvtJoinedGroupMemberConnecting user gInfo m announcedMember'
toView $ CEvtJoinedGroupMemberConnecting user gInfo' m announcedMember'
getMemNewChatScope announcedMember = case msgScope_ of
Nothing -> pure (announcedMember, Nothing)
Just (MSMember _) -> do
@@ -2571,8 +2587,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| otherwise = do
withStore' $ \db -> updateGroupMemberRole db user member memRole
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView gInfo'' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView cInfo ci
toView CEvtMemberRole {user, groupInfo = gInfo'', byMember = m', member = member {memberRole = memRole}, fromRole, toRole = memRole}
checkHostRole :: GroupMember -> GroupMemberRole -> CM ()
@@ -2600,8 +2616,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
toggleNtf bm' (not blocked)
let ciContent = CIRcvGroupEvent $ RGEMemberBlocked bmId (fromLocalProfile bmp) blocked
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs ciContent
groupMsgToView gInfo' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs ciContent
groupMsgToView cInfo ci
toView CEvtMemberBlockedForAll {user, groupInfo = gInfo', byMember = m', member = bm, blocked}
Left (SEGroupMemberNotFoundByMemberId _) -> do
bm <- createUnknownMember gInfo memId
@@ -2685,8 +2701,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| otherwise = a
deleteMemberItem gEvent = do
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView gInfo' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView cInfo ci
deleteMessages :: MsgDirectionI d => GroupMember -> SMsgDirection d -> CM ()
deleteMessages delMem msgDir
| groupFeatureMemberAllowed SGFFullDelete m gInfo = deleteGroupMemberCIs user gInfo delMem m msgDir
@@ -2698,8 +2714,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- member record is not deleted to allow creation of "member left" chat item
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemLeft
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
groupMsgToView gInfo' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
groupMsgToView cInfo ci
toView $ CEvtLeftMember user gInfo' m' {memberStatus = GSMemLeft}
xGrpDel :: GroupInfo -> GroupMember -> RcvMessage -> UTCTime -> CM ()
@@ -2712,8 +2728,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- member records are not deleted to keep history
deleteMembersConnections user ms
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo m
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEGroupDeleted)
groupMsgToView gInfo'' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEGroupDeleted)
groupMsgToView cInfo ci
toView $ CEvtGroupDeleted user gInfo'' {membership = membership {memberStatus = GSMemGroupDeleted}} m'
xGrpInfo :: GroupInfo -> GroupMember -> GroupProfile -> RcvMessage -> UTCTime -> CM ()
@@ -2726,8 +2742,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
toView $ CEvtGroupUpdated user g g'' (Just m')
let cd = CDGroupRcv g'' scopeInfo m'
unless (sameGroupProfileInfo p p') $ do
ci <- saveRcvChatItemNoParse user cd msg brokerTs (CIRcvGroupEvent $ RGEGroupUpdated p')
groupMsgToView g'' scopeInfo ci
(ci, cInfo) <- saveRcvChatItemNoParse user cd msg brokerTs (CIRcvGroupEvent $ RGEGroupUpdated p')
groupMsgToView cInfo ci
createGroupFeatureChangedItems user cd CIRcvGroupFeature g g''
Just _ -> updateGroupPrefs_ g m $ fromMaybe defaultBusinessGroupPrefs $ groupPreferences p'
@@ -2787,8 +2803,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
createInternalChatItem user (CDGroupRcv g' scopeInfo m'') (CIRcvGroupEvent RGEMemberCreatedContact) Nothing
toView $ CEvtNewMemberContactReceivedInv user mCt' g' m''
forM_ mContent_ $ \mc -> do
ci <- saveRcvChatItem user (CDDirectRcv mCt') msg brokerTs (CIRcvMsgContent mc, msgContentTexts mc)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat mCt') ci]
(ci, cInfo) <- saveRcvChatItem user (CDDirectRcv mCt') msg brokerTs (CIRcvMsgContent mc, msgContentTexts mc)
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
securityCodeChanged :: Contact -> CM ()
securityCodeChanged ct = do
+1 -2
View File
@@ -350,14 +350,13 @@ data ChatStats = ChatStats
{ unreadCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
unreadMentions :: Int, -- returned both in /_get chat initial API and in /_get chats API
reportsCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
supportChatsUnreadCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
minUnreadItemId :: ChatItemId,
unreadChat :: Bool
}
deriving (Show)
emptyChatStats :: ChatStats
emptyChatStats = ChatStats 0 0 0 0 0 False
emptyChatStats = ChatStats 0 0 0 0 False
data NavigationInfo = NavigationInfo
{ afterUnread :: Int,
+2 -1
View File
@@ -137,7 +137,8 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
+43 -5
View File
@@ -80,6 +80,8 @@ module Simplex.Chat.Store.Groups
updateGroupMemberStatus,
updateGroupMemberStatusById,
updateGroupMemberAccepted,
updateGroupMembersRequireAttention,
increaseGroupMembersRequireAttention,
createNewGroupMember,
checkGroupMemberHasItems,
deleteGroupMember,
@@ -279,7 +281,8 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
@@ -362,7 +365,8 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
chatTags = [],
chatItemTTL = Nothing,
uiThemes = Nothing,
customData = Nothing
customData = Nothing,
membersRequireAttention = 0
}
-- | creates a new group record for the group the current user was invited to, or returns an existing one
@@ -432,7 +436,8 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
chatTags = [],
chatItemTTL = Nothing,
uiThemes = Nothing,
customData = Nothing
customData = Nothing,
membersRequireAttention = 0
},
groupMemberId
)
@@ -768,7 +773,8 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
SELECT
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, 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,
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.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
@@ -1224,6 +1230,37 @@ updateGroupMemberAccepted db User {userId} m@GroupMember {groupMemberId} status
(status, role, currentTs, userId, groupMemberId)
pure m {memberStatus = status, memberRole = role, updatedAt = currentTs}
updateGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> GroupMember -> GroupMember -> IO GroupInfo
updateGroupMembersRequireAttention db user@User {userId} g@GroupInfo {groupId, membersRequireAttention} member member'
| nowRequires && not didRequire =
increaseGroupMembersRequireAttention db user g
| not nowRequires && didRequire = do
DB.execute
db
[sql|
UPDATE groups
SET members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
|]
(userId, groupId)
pure g {membersRequireAttention = membersRequireAttention - 1}
| otherwise = pure g
where
didRequire = gmRequiresAttention member
nowRequires = gmRequiresAttention member'
increaseGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> IO GroupInfo
increaseGroupMembersRequireAttention db User {userId} g@GroupInfo {groupId, membersRequireAttention} = do
DB.execute
db
[sql|
UPDATE groups
SET members_require_attention = members_require_attention + 1
WHERE user_id = ? AND group_id = ?
|]
(userId, groupId)
pure g {membersRequireAttention = membersRequireAttention + 1}
-- | add new member with profile
createNewGroupMember :: DB.Connection -> User -> GroupInfo -> GroupMember -> MemberInfo -> GroupMemberCategory -> GroupMemberStatus -> ExceptT StoreError IO GroupMember
createNewGroupMember db user gInfo invitingMember memInfo@MemberInfo {profile} memCategory memStatus = do
@@ -1561,7 +1598,8 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
+150 -97
View File
@@ -369,56 +369,103 @@ type NewQuoteRow = (Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bo
data MemberAttention = MAInc Int | MAReset
deriving (Show)
updateChatTsStats :: DB.Connection -> User -> ChatDirection c d -> UTCTime -> Maybe (Int, MemberAttention, Int) -> IO ()
updateChatTsStats db User {userId} chatDirection chatTs chatStats_ = case toChatInfo chatDirection of
DirectChat Contact {contactId} ->
updateChatTsStats :: DB.Connection -> VersionRangeChat -> User -> ChatDirection c d -> UTCTime -> Maybe (Int, MemberAttention, Int) -> IO (ChatInfo c)
updateChatTsStats db vr user@User {userId} chatDirection chatTs chatStats_ = case toChatInfo chatDirection of
DirectChat ct@Contact {contactId} -> do
DB.execute
db
"UPDATE contacts SET chat_ts = ?, chat_deleted = 0 WHERE user_id = ? AND contact_id = ?"
(chatTs, userId, contactId)
GroupChat GroupInfo {groupId, membership} scopeInfo -> do
pure $ DirectChat ct {chatTs = Just chatTs}
GroupChat g@GroupInfo {groupId} Nothing -> do
DB.execute
db
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
(chatTs, userId, groupId)
forM_ scopeInfo $ \GCSIMemberSupport {groupMember_} -> do
let gmId = groupMemberId' $ fromMaybe membership groupMember_
case chatStats_ of
Nothing ->
DB.execute
db
"UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?"
(chatTs, gmId)
Just (unread, MAInc unanswered, mentions) ->
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = support_chat_items_member_attention + ?,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
|]
(chatTs, unread, unanswered, mentions, gmId)
Just (unread, MAReset, mentions) ->
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = 0,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
|]
(chatTs, unread, mentions, gmId)
LocalChat NoteFolder {noteFolderId} ->
pure $ GroupChat g {chatTs = Just chatTs} Nothing
GroupChat g@GroupInfo {groupId, membership, membersRequireAttention} (Just GCSIMemberSupport {groupMember_}) ->
case groupMember_ of
Nothing -> do
membership' <- updateGMStats membership
DB.execute
db
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
(chatTs, userId, groupId)
pure $ GroupChat g {membership = membership', chatTs = Just chatTs} (Just $ GCSIMemberSupport Nothing)
Just member -> do
member' <- updateGMStats member
let didRequire = gmRequiresAttention member
nowRequires = gmRequiresAttention member'
if
| nowRequires && not didRequire -> do
DB.execute
db
[sql|
UPDATE groups
SET chat_ts = ?,
members_require_attention = members_require_attention + 1
WHERE user_id = ? AND group_id = ?
|]
(chatTs, userId, groupId)
pure $ GroupChat g {membersRequireAttention = membersRequireAttention + 1, chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
| not nowRequires && didRequire -> do
DB.execute
db
[sql|
UPDATE groups
SET chat_ts = ?,
members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
|]
(chatTs, userId, groupId)
pure $ GroupChat g {membersRequireAttention = membersRequireAttention - 1, chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
| otherwise -> do
DB.execute
db
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
(chatTs, userId, groupId)
pure $ GroupChat g {chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
where
updateGMStats m@GroupMember {groupMemberId} = do
case chatStats_ of
Nothing ->
DB.execute
db
"UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?"
(chatTs, groupMemberId)
Just (unread, MAInc unanswered, mentions) ->
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = support_chat_items_member_attention + ?,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
|]
(chatTs, unread, unanswered, mentions, groupMemberId)
Just (unread, MAReset, mentions) ->
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = 0,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
|]
(chatTs, unread, mentions, groupMemberId)
m_ <- runExceptT $ getGroupMemberById db vr user groupMemberId
pure $ either (const m) id m_ -- Left shouldn't happen, but types require it
LocalChat nf@NoteFolder {noteFolderId} -> do
DB.execute
db
"UPDATE note_folders SET chat_ts = ? WHERE user_id = ? AND note_folder_id = ?"
(chatTs, userId, noteFolderId)
_ -> pure ()
pure $ LocalChat nf {chatTs = chatTs}
cInfo -> pure cInfo
setSupportChatTs :: DB.Connection -> GroupMemberId -> UTCTime -> IO ()
setSupportChatTs db groupMemberId chatTs =
@@ -636,13 +683,13 @@ type ChatStatsRow = (Int, ChatItemId, BoolInt)
toChatStats :: ChatStatsRow -> ChatStats
toChatStats (unreadCount, minUnreadItemId, BI unreadChat) =
ChatStats {unreadCount, unreadMentions = 0, reportsCount = 0, supportChatsUnreadCount = 0, minUnreadItemId, unreadChat}
ChatStats {unreadCount, unreadMentions = 0, reportsCount = 0, minUnreadItemId, unreadChat}
type GroupStatsRow = (Int, Int, Int, Int, ChatItemId, BoolInt)
type GroupStatsRow = (Int, Int, Int, ChatItemId, BoolInt)
toGroupStats :: GroupStatsRow -> ChatStats
toGroupStats (unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, BI unreadChat) =
ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat}
toGroupStats (unreadCount, unreadMentions, reportsCount, minUnreadItemId, BI unreadChat) =
ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat}
findDirectChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
findDirectChatPreviews_ db User {userId} pagination clq =
@@ -763,7 +810,6 @@ findGroupChatPreviews_ db User {userId} pagination clq =
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -780,16 +826,8 @@ findGroupChatPreviews_ db User {userId} pagination clq =
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|]
baseParams = (userId, userId, CISRcvNew, userId, MCReport_, BI False, userId, CISRcvNew)
baseParams = (userId, userId, CISRcvNew, userId, MCReport_, BI False)
getPreviews = case clq of
CLQFilters {favorite = False, unread = False} -> do
let q = baseQuery <> " WHERE g.user_id = ?"
@@ -1494,16 +1532,14 @@ getGroupChatInitial_ db user g scopeInfo_ contentFilter count = do
where
getStats minUnreadItemId (unreadCount, unreadMentions) = do
reportsCount <- getGroupReportsCount_ db user g False
supportChatsUnreadCount <- getGroupSupportChatsUnreadCount_ db user g
pure ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat = False}
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
getGroupStats_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> ExceptT StoreError IO ChatStats
getGroupStats_ db user g scopeInfo_ = do
minUnreadItemId <- fromMaybe 0 <$> getGroupMinUnreadId_ db user g scopeInfo_ Nothing
(unreadCount, unreadMentions) <- getGroupUnreadCount_ db user g scopeInfo_ Nothing
reportsCount <- liftIO $ getGroupReportsCount_ db user g False
supportChatsUnreadCount <- liftIO $ getGroupSupportChatsUnreadCount_ db user g
pure ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat = False}
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
getGroupMinUnreadId_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ExceptT StoreError IO (Maybe ChatItemId)
getGroupMinUnreadId_ db user g scopeInfo_ contentFilter =
@@ -1527,14 +1563,6 @@ getGroupReportsCount_ db User {userId} GroupInfo {groupId} archived =
"SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0"
(userId, groupId, MCReport_, BI archived)
getGroupSupportChatsUnreadCount_ :: DB.Connection -> User -> GroupInfo -> IO Int
getGroupSupportChatsUnreadCount_ db User {userId} GroupInfo {groupId} =
fromOnly . head
<$> DB.query
db
"SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NOT NULL AND item_status = ?"
(userId, groupId, CISRcvNew)
queryUnreadGroupItems :: FromRow r => DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> Query -> Query -> ExceptT StoreError IO [r]
queryUnreadGroupItems db User {userId} GroupInfo {groupId} scopeInfo_ contentFilter baseQuery orderLimit =
case (scopeInfo_, contentFilter) of
@@ -1954,14 +1982,14 @@ getGroupUnreadTimedItems db User {userId} groupId =
|]
(userId, groupId, CISRcvNew)
updateGroupChatItemsReadList :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScope -> NonEmpty ChatItemId -> IO [(ChatItemId, Int)]
updateGroupChatItemsReadList db User {userId} GroupInfo {groupId, membership} scope itemIds = do
currentTs <- getCurrentTime
updateGroupChatItemsReadList :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> Maybe GroupChatScope -> NonEmpty ChatItemId -> ExceptT StoreError IO ([(ChatItemId, Int)], GroupInfo)
updateGroupChatItemsReadList db vr user@User {userId} g@GroupInfo {groupId, membership, membersRequireAttention} scope itemIds = do
currentTs <- liftIO getCurrentTime
-- Possible improvement is to differentiate retrieval queries for each scope,
-- but we rely on UI to not pass item IDs from incorrect scope.
readItemsData <- catMaybes . L.toList <$> mapM (getUpdateGroupItem currentTs) itemIds
updateChatStats readItemsData
pure $ timedItems readItemsData
readItemsData <- liftIO $ catMaybes . L.toList <$> mapM (getUpdateGroupItem currentTs) itemIds
g' <- updateChatStats readItemsData
pure (timedItems readItemsData, g')
where
getUpdateGroupItem :: UTCTime -> ChatItemId -> IO (Maybe (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt))
getUpdateGroupItem currentTs itemId =
@@ -1974,36 +2002,61 @@ updateGroupChatItemsReadList db User {userId} GroupInfo {groupId, membership} sc
RETURNING chat_item_id, timed_ttl, timed_delete_at, group_member_id, user_mention
|]
(CISRcvRead, currentTs, userId, groupId, CISRcvNew, itemId)
updateChatStats :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> IO ()
updateChatStats :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> ExceptT StoreError IO GroupInfo
updateChatStats readItemsData = case scope of
Nothing -> pure ()
Just GCSMemberSupport {groupMemberId_} -> do
let unread = length readItemsData
(unanswered, mentions) = decStats
gmId = fromMaybe (groupMemberId' membership) groupMemberId_
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_items_unread = support_chat_items_unread - ?,
support_chat_items_member_attention = support_chat_items_member_attention - ?,
support_chat_items_mentions = support_chat_items_mentions - ?
WHERE group_member_id = ?
|]
(unread, unanswered, mentions, gmId)
Nothing -> pure g
Just GCSMemberSupport {groupMemberId_} -> case groupMemberId_ of
Nothing -> do
membership' <- updateGMStats membership
pure g {membership = membership'}
Just groupMemberId -> do
member <- getGroupMemberById db vr user groupMemberId
member' <- updateGMStats member
let didRequire = gmRequiresAttention member
nowRequires = gmRequiresAttention member'
if (not nowRequires && didRequire)
then do
liftIO $
DB.execute
db
[sql|
UPDATE groups
SET members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
|]
(userId, groupId)
pure g {membersRequireAttention = membersRequireAttention - 1}
else
pure g
where
decStats :: (Int, Int)
decStats = foldl' countItem (0, 0) readItemsData
updateGMStats GroupMember {groupMemberId} = do
let unread = length readItemsData
(unanswered, mentions) = decStats
liftIO $
DB.execute
db
[sql|
UPDATE group_members
SET support_chat_items_unread = support_chat_items_unread - ?,
support_chat_items_member_attention = support_chat_items_member_attention - ?,
support_chat_items_mentions = support_chat_items_mentions - ?
WHERE group_member_id = ?
|]
(unread, unanswered, mentions, groupMemberId)
getGroupMemberById db vr user groupMemberId
where
countItem :: (Int, Int) -> (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt) -> (Int, Int)
countItem (!unanswered, !mentions) (_, _, _, itemGMId_, userMention_) =
let unanswered' = case (groupMemberId_, itemGMId_) of
(Just scopeGMId, Just itemGMId) | itemGMId == scopeGMId -> unanswered + 1
_ -> unanswered
mentions' = case userMention_ of
Just (BI True) -> mentions + 1
_ -> mentions
in (unanswered', mentions')
decStats :: (Int, Int)
decStats = foldl' countItem (0, 0) readItemsData
where
countItem :: (Int, Int) -> (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt) -> (Int, Int)
countItem (!unanswered, !mentions) (_, _, _, itemGMId_, userMention_) =
let unanswered' = case (groupMemberId_, itemGMId_) of
(Just scopeGMId, Just itemGMId) | itemGMId == scopeGMId -> unanswered + 1
_ -> unanswered
mentions' = case userMention_ of
Just (BI True) -> mentions + 1
_ -> mentions
in (unanswered', mentions')
timedItems :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> [(ChatItemId, Int)]
timedItems = foldl' addTimedItem []
where
@@ -17,6 +17,8 @@ ALTER TABLE group_members ADD COLUMN support_chat_items_unread BIGINT NOT NULL D
ALTER TABLE group_members ADD COLUMN support_chat_items_member_attention BIGINT NOT NULL DEFAULT 0;
ALTER TABLE group_members ADD COLUMN support_chat_items_mentions BIGINT NOT NULL DEFAULT 0;
ALTER TABLE groups ADD COLUMN members_require_attention BIGINT NOT NULL DEFAULT 0;
ALTER TABLE chat_items ADD COLUMN group_scope_tag TEXT;
ALTER TABLE chat_items ADD COLUMN group_scope_group_member_id BIGINT REFERENCES group_members(group_member_id) ON DELETE CASCADE;
@@ -53,6 +55,8 @@ DROP INDEX idx_chat_items_group_scope_group_member_id;
ALTER TABLE chat_items DROP COLUMN group_scope_tag;
ALTER TABLE chat_items DROP COLUMN group_scope_group_member_id;
ALTER TABLE groups DROP COLUMN members_require_attention;
ALTER TABLE group_members DROP COLUMN support_chat_ts;
ALTER TABLE group_members DROP COLUMN support_chat_items_unread;
ALTER TABLE group_members DROP COLUMN support_chat_items_member_attention;
@@ -15,6 +15,8 @@ ALTER TABLE group_members ADD COLUMN support_chat_items_unread INTEGER NOT NULL
ALTER TABLE group_members ADD COLUMN support_chat_items_member_attention INTEGER NOT NULL DEFAULT 0;
ALTER TABLE group_members ADD COLUMN support_chat_items_mentions INTEGER NOT NULL DEFAULT 0;
ALTER TABLE groups ADD COLUMN members_require_attention INTEGER NOT NULL DEFAULT 0;
ALTER TABLE chat_items ADD COLUMN group_scope_tag TEXT;
ALTER TABLE chat_items ADD COLUMN group_scope_group_member_id INTEGER REFERENCES group_members(group_member_id) ON DELETE CASCADE;
@@ -50,6 +52,8 @@ DROP INDEX idx_chat_items_group_scope_group_member_id;
ALTER TABLE chat_items DROP COLUMN group_scope_tag;
ALTER TABLE chat_items DROP COLUMN group_scope_group_member_id;
ALTER TABLE groups DROP COLUMN members_require_attention;
ALTER TABLE group_members DROP COLUMN support_chat_ts;
ALTER TABLE group_members DROP COLUMN support_chat_items_unread;
ALTER TABLE group_members DROP COLUMN support_chat_items_member_attention;
@@ -1,3 +1,39 @@
Query:
UPDATE groups
SET members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_items_unread = support_chat_items_unread - ?,
support_chat_items_member_attention = support_chat_items_member_attention - ?,
support_chat_items_mentions = support_chat_items_mentions - ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE groups
SET chat_ts = ?,
members_require_attention = members_require_attention + 1
WHERE user_id = ? AND group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE groups
SET chat_ts = ?,
members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
INSERT INTO group_members
( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id,
@@ -36,7 +72,8 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
@@ -81,6 +118,28 @@ Query:
Plan:
SEARCH contact_requests USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = 0,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = support_chat_items_member_attention + ?,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
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,
@@ -621,38 +680,6 @@ Query:
Plan:
SEARCH chat_items USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_items_unread = support_chat_items_unread - ?,
support_chat_items_member_attention = support_chat_items_member_attention - ?,
support_chat_items_mentions = support_chat_items_mentions - ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = 0,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_ts = ?,
support_chat_items_unread = support_chat_items_unread + ?,
support_chat_items_member_attention = support_chat_items_member_attention + ?,
support_chat_items_mentions = support_chat_items_mentions + ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
DELETE FROM chat_item_reactions
WHERE contact_id = ? AND shared_msg_id = ? AND reaction_sent = ? AND reaction = ?
@@ -840,7 +867,8 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
-- GroupInfo {membership}
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
@@ -887,7 +915,8 @@ Query:
SELECT
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, 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,
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.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
@@ -1270,16 +1299,6 @@ Query:
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET support_chat_items_unread = 0,
support_chat_items_member_attention = 0,
support_chat_items_mentions = 0
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_profiles
SET display_name = ?, full_name = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
@@ -1294,6 +1313,14 @@ SEARCH group_profiles USING INTEGER PRIMARY KEY (rowid=?)
LIST SUBQUERY 1
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE groups
SET members_require_attention = members_require_attention - 1
WHERE user_id = ? AND group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE user_contact_links
SET auto_accept = ?, business_address = ?, auto_accept_incognito = ?, auto_reply_msg_content = ?
@@ -1948,7 +1975,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -1965,14 +1991,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
WHERE g.user_id = ?
@@ -1988,13 +2006,10 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2012,7 +2027,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2029,14 +2043,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND (g.favorite = 1
@@ -2047,12 +2053,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2070,7 +2073,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2087,14 +2089,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
@@ -2104,12 +2098,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2127,7 +2118,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2144,14 +2134,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
@@ -2161,12 +2143,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2184,7 +2163,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2201,14 +2179,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
@@ -2218,12 +2188,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2241,7 +2208,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2258,14 +2224,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND g.favorite = 1
@@ -2275,12 +2233,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2298,7 +2253,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2315,14 +2269,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND g.favorite = 1
@@ -2332,12 +2278,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2355,7 +2298,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2372,14 +2314,6 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ?
AND g.favorite = 1
@@ -2389,12 +2323,9 @@ MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2412,7 +2343,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2429,26 +2359,15 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ? AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2466,7 +2385,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2483,26 +2401,15 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ? AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -2520,7 +2427,6 @@ Query:
COALESCE(ChatStats.UnreadCount, 0),
COALESCE(ChatStats.UnreadMentions, 0),
COALESCE(ReportCount.Count, 0),
COALESCE(SupportChatsUnreadCount.Count, 0),
COALESCE(ChatStats.MinUnread, 0),
g.unread_chat
FROM groups g
@@ -2537,26 +2443,15 @@ Query:
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
GROUP BY group_id
) ReportCount ON ReportCount.group_id = g.group_id
LEFT JOIN (
SELECT group_id, COUNT(1) AS Count
FROM chat_items
WHERE user_id = ? AND group_id IS NOT NULL
AND group_scope_tag IS NOT NULL
AND item_status = ?
GROUP BY group_id
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
WHERE g.user_id = ? ORDER BY g.chat_ts DESC LIMIT ?
Plan:
MATERIALIZE ChatStats
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
MATERIALIZE ReportCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
MATERIALIZE SupportChatsUnreadCount
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
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
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
CORRELATED SCALAR SUBQUERY 1
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
@@ -4526,6 +4421,14 @@ Query:
Plan:
SEARCH group_snd_item_statuses USING INDEX idx_group_snd_item_statuses_chat_item_id_group_member_id (chat_item_id=? AND group_member_id=?)
Query:
UPDATE groups
SET members_require_attention = members_require_attention + 1
WHERE user_id = ? AND group_id = ?
Plan:
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE groups
SET via_group_link_uri_hash = (SELECT via_contact_uri_hash FROM connections WHERE connection_id = ?)
@@ -4625,7 +4528,8 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, 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,
-- 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,
@@ -4648,7 +4552,8 @@ Query:
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, 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,
-- 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,
@@ -5638,10 +5543,6 @@ Query: SELECT COUNT(1) FROM chat_item_versions WHERE chat_item_id = ?
Plan:
SEARCH chat_item_versions USING COVERING INDEX idx_chat_item_versions_chat_item_id (chat_item_id=?)
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NOT NULL AND item_status = ?
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id=? AND group_scope_tag>?)
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id=? AND msg_content_tag=? AND item_deleted=? AND item_sent=?)
@@ -134,7 +134,8 @@ CREATE TABLE groups(
business_xcontact_id BLOB NULL,
customer_member_id BLOB NULL,
chat_item_ttl INTEGER,
local_alias TEXT DEFAULT '', -- received
local_alias TEXT DEFAULT '',
members_require_attention INTEGER NOT NULL DEFAULT 0, -- received
FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE
+5 -4
View File
@@ -579,18 +579,18 @@ safeDeleteLDN db User {userId} localDisplayName = do
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64) :. GroupMemberRow
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64, Int) :. GroupMemberRow
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64)
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL) :. userMemberRow) =
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL, membersRequireAttention) :. 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, description, image, groupPreferences, memberAdmission}
businessChat = toBusinessChatInfo businessRow
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, chatItemTTL, uiThemes, customData}
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, chatItemTTL, uiThemes, customData, membersRequireAttention}
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, preferences) :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions)) =
@@ -622,7 +622,8 @@ groupInfoQuery =
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, 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,
-- 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,
+9 -1
View File
@@ -421,7 +421,8 @@ data GroupInfo = GroupInfo
chatTags :: [ChatTagId],
chatItemTTL :: Maybe Int64,
uiThemes :: Maybe UIThemeEntityOverrides,
customData :: Maybe CustomData
customData :: Maybe CustomData,
membersRequireAttention :: Int
}
deriving (Eq, Show)
@@ -854,6 +855,13 @@ data GroupSupportChat = GroupSupportChat
}
deriving (Eq, Show)
gmRequiresAttention :: GroupMember -> Bool
gmRequiresAttention m@GroupMember {supportChat} =
memberPending m || maybe False supportChatAttention supportChat
where
supportChatAttention GroupSupportChat {memberAttention, mentions} =
memberAttention > 0 || mentions > 0
data GroupMemberRef = GroupMemberRef {groupMemberId :: Int64, profile :: Profile}
deriving (Eq, Show)
+1
View File
@@ -192,6 +192,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
CRSentConfirmation u _ -> ttyUser u ["confirmation sent!"]
CRSentInvitation u _ customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
CRSentInvitationToContact u _c customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
CRItemsReadForChat u chatId -> ttyUser u ["items read for chat"]
CRContactDeleted u c -> ttyUser u [ttyContact' c <> ": contact is deleted"]
CRChatCleared u chatInfo -> ttyUser u $ viewChatCleared chatInfo
CRAcceptingContactRequest u c -> ttyUser u $ viewAcceptingContactRequest c