diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index ea7f127854..a420471331 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -1579,6 +1579,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = | memberRole >= GRAuthor || memberPending m -> a | otherwise -> messageError "member is not allowed to send messages" $> Nothing + memberCanComment :: Maybe GroupMember -> CM (Maybe a) -> CM (Maybe a) + memberCanComment Nothing a = a -- channel post from owner via relay - role precheck on the relay + memberCanComment (Just GroupMember {memberRole}) a + | memberRole >= GRCommenter = a + | otherwise = messageError "member is not allowed to comment" $> Nothing + processConnMERR :: ConnectionEntity -> Connection -> AgentErrorType -> CM () processConnMERR connEntity conn err = do case err of @@ -2020,25 +2026,29 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = Just m@GroupMember {memberId} -> do (gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m content msgScope_ channelMsgInfo_ <- resolveCommentParent gInfo' parent_ - if blockedByAdmin m' - then createBlockedByAdmin gInfo' (Just m') scopeInfo $> Nothing - else case prohibitedGroupContent gInfo' m' scopeInfo channelMsgInfo_ content ft_ fInv_ False of - Just GFComments - | not forwarded -> messageError "channel comment prohibited" $> Nothing - Just f -> rejected gInfo' (Just m') scopeInfo f $> Nothing - Nothing -> do - now <- liftIO getCurrentTime - if commentsClosed gInfo' channelMsgInfo_ now - then messageError "channel post comments are closed" $> Nothing - else - withStore' (\db -> getCIModeration db vr user gInfo' memberId sharedMsgId_) >>= \case - Just ciModeration -> do - applyModeration gInfo' m' scopeInfo ciModeration - withStore' $ \db -> deleteCIModeration db gInfo' memberId sharedMsgId_ - pure Nothing - Nothing -> do - createContentItem gInfo' (Just m') scopeInfo channelMsgInfo_ - pure $ Just $ infoToDeliveryContext gInfo' scopeInfo sentAsGroup + let withCommentRoleCheck = case channelMsgInfo_ of + Just _ -> memberCanComment (Just m') + Nothing -> id + withCommentRoleCheck $ + if blockedByAdmin m' + then createBlockedByAdmin gInfo' (Just m') scopeInfo $> Nothing + else case prohibitedGroupContent gInfo' m' scopeInfo channelMsgInfo_ content ft_ fInv_ False of + Just GFComments + | not forwarded -> messageError "channel comment prohibited" $> Nothing + Just f -> rejected gInfo' (Just m') scopeInfo f $> Nothing + Nothing -> do + now <- liftIO getCurrentTime + if commentsClosed gInfo' channelMsgInfo_ now + then messageError "channel post comments are closed" $> Nothing + else + withStore' (\db -> getCIModeration db vr user gInfo' memberId sharedMsgId_) >>= \case + Just ciModeration -> do + applyModeration gInfo' m' scopeInfo ciModeration + withStore' $ \db -> deleteCIModeration db gInfo' memberId sharedMsgId_ + pure Nothing + Nothing -> do + createContentItem gInfo' (Just m') scopeInfo channelMsgInfo_ + pure $ Just $ infoToDeliveryContext gInfo' scopeInfo sentAsGroup where rejected gInfo' m' scopeInfo f = newChatItem gInfo' m' scopeInfo Nothing (ciContentNoParse $ CIRcvGroupFeatureRejected f) Nothing Nothing False timed_ gInfo' = if forwarded then rcvCITimed_ (Just Nothing) itemTTL else rcvGroupCITimed gInfo' itemTTL diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 67271eadf2..0c15253956 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -1776,7 +1776,7 @@ getGroupChatAround' db user g scopeInfo parentChatItemId_ contentFilter aroundId where getNavInfo cis_ = case cis_ of [] -> pure $ NavigationInfo 0 0 - cis -> getGroupNavInfo_ db user g (last cis) + cis -> getGroupNavInfo_ db user g scopeInfo parentChatItemId_ (last cis) getGroupChatInitial_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe ChatItemId -> Maybe MsgContentTag -> Int -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo) getGroupChatInitial_ db user g scopeInfo_ parentChatItemId_ contentFilter count = do @@ -1862,56 +1862,154 @@ queryUnreadGroupItems db User {userId} GroupInfo {groupId} scopeInfo_ contentFil (Just _scope, Just _mcTag) -> throwError $ SEInternalError "group scope and content filter are not supported together" -getGroupNavInfo_ :: DB.Connection -> User -> GroupInfo -> CChatItem 'CTGroup -> IO NavigationInfo -getGroupNavInfo_ db User {userId} GroupInfo {groupId} afterCI = do +getGroupNavInfo_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe ChatItemId -> CChatItem 'CTGroup -> IO NavigationInfo +getGroupNavInfo_ db User {userId} GroupInfo {groupId} scopeInfo_ parentChatItemId_ afterCI = do afterUnread <- getAfterUnreadCount afterTotal <- getAfterTotalCount pure NavigationInfo {afterUnread, afterTotal} where getAfterUnreadCount :: IO Int getAfterUnreadCount = - fromOnly . head - <$> DB.query - db - [sql| - SELECT COUNT(1) - FROM ( - SELECT 1 - FROM chat_items - WHERE user_id = ? AND group_id = ? AND item_status = ? - AND item_ts > ? - UNION ALL - SELECT 1 - FROM chat_items - WHERE user_id = ? AND group_id = ? AND item_status = ? - AND item_ts = ? AND chat_item_id > ? - ) ci - |] - ( (userId, groupId, CISRcvNew, chatItemTs afterCI) - :. (userId, groupId, CISRcvNew, chatItemTs afterCI, cChatItemId afterCI) - ) + fromOnly . head <$> case (scopeInfo_, parentChatItemId_) of + (Just _, Just _) -> + error "getGroupNavInfo_: scope and parent are mutually exclusive" + (Nothing, Just pId) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts > ? + AND parent_chat_item_id = ? + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id = ? + ) ci + |] + ( (userId, groupId, CISRcvNew, chatItemTs afterCI, pId) + :. (userId, groupId, CISRcvNew, chatItemTs afterCI, cChatItemId afterCI, pId) + ) + (Nothing, Nothing) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts > ? + AND parent_chat_item_id IS NULL AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id IS NULL AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL + ) ci + |] + ( (userId, groupId, CISRcvNew, chatItemTs afterCI) + :. (userId, groupId, CISRcvNew, chatItemTs afterCI, cChatItemId afterCI) + ) + (Just GCSIMemberSupport {groupMember_ = m}, Nothing) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts > ? + AND parent_chat_item_id IS NULL AND group_scope_tag = ? AND group_scope_group_member_id IS NOT DISTINCT FROM ? + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? AND item_status = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id IS NULL AND group_scope_tag = ? AND group_scope_group_member_id IS NOT DISTINCT FROM ? + ) ci + |] + ( (userId, groupId, CISRcvNew, chatItemTs afterCI, GCSTMemberSupport_, groupMemberId' <$> m) + :. (userId, groupId, CISRcvNew, chatItemTs afterCI, cChatItemId afterCI, GCSTMemberSupport_, groupMemberId' <$> m) + ) getAfterTotalCount :: IO Int getAfterTotalCount = - fromOnly . head - <$> DB.query - db - [sql| - SELECT COUNT(1) - FROM ( - SELECT 1 - FROM chat_items - WHERE user_id = ? AND group_id = ? - AND item_ts > ? - UNION ALL - SELECT 1 - FROM chat_items - WHERE user_id = ? AND group_id = ? - AND item_ts = ? AND chat_item_id > ? - ) ci - |] - ( (userId, groupId, chatItemTs afterCI) - :. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI) - ) + fromOnly . head <$> case (scopeInfo_, parentChatItemId_) of + (Just _, Just _) -> + error "getGroupNavInfo_: scope and parent are mutually exclusive" + (Nothing, Just pId) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts > ? + AND parent_chat_item_id = ? + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id = ? + ) ci + |] + ( (userId, groupId, chatItemTs afterCI, pId) + :. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI, pId) + ) + (Nothing, Nothing) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts > ? + AND parent_chat_item_id IS NULL AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id IS NULL AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL + ) ci + |] + ( (userId, groupId, chatItemTs afterCI) + :. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI) + ) + (Just GCSIMemberSupport {groupMember_ = m}, Nothing) -> + DB.query + db + [sql| + SELECT COUNT(1) + FROM ( + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts > ? + AND parent_chat_item_id IS NULL AND group_scope_tag = ? AND group_scope_group_member_id IS NOT DISTINCT FROM ? + UNION ALL + SELECT 1 + FROM chat_items + WHERE user_id = ? AND group_id = ? + AND item_ts = ? AND chat_item_id > ? + AND parent_chat_item_id IS NULL AND group_scope_tag = ? AND group_scope_group_member_id IS NOT DISTINCT FROM ? + ) ci + |] + ( (userId, groupId, chatItemTs afterCI, GCSTMemberSupport_, groupMemberId' <$> m) + :. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI, GCSTMemberSupport_, groupMemberId' <$> m) + ) getLocalChat :: DB.Connection -> User -> Int64 -> Maybe MsgContentTag -> ChatPagination -> Maybe Text -> ExceptT StoreError IO (Chat 'CTLocal, Maybe NavigationInfo) getLocalChat db user folderId contentFilter pagination search_ = do