mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 12:19:54 +00:00
core, ui: update group scope unread stats on item deletions; create reports as read (reports use a different attention mechanism) (#5930)
This commit is contained in:
parent
f162f96be8
commit
e84016e1b9
12 changed files with 186 additions and 90 deletions
|
@ -2206,6 +2206,9 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
|||
m.decreaseGroupReportsCounter(item.deletedChatItem.chatInfo.id)
|
||||
}
|
||||
}
|
||||
if let updatedChatInfo = items.last?.deletedChatItem.chatInfo {
|
||||
m.updateChatInfo(updatedChatInfo)
|
||||
}
|
||||
}
|
||||
case let .groupChatItemsDeleted(user, groupInfo, chatItemIDs, _, member_):
|
||||
await groupChatItemsDeleted(user, groupInfo, chatItemIDs, member_)
|
||||
|
|
|
@ -2277,6 +2277,7 @@ struct ChatView: View {
|
|||
if deletedItem.isActiveReport {
|
||||
m.decreaseGroupReportsCounter(chat.chatInfo.id)
|
||||
}
|
||||
m.updateChatInfo(itemDeletion.deletedChatItem.chatInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2466,6 +2467,9 @@ private func deleteMessages(_ chat: Chat, _ deletingItems: [Int64], _ mode: CIDe
|
|||
ChatModel.shared.decreaseGroupReportsCounter(chat.chatInfo.id)
|
||||
}
|
||||
}
|
||||
if let updatedChatInfo = deletedItems.last?.deletedChatItem.chatInfo {
|
||||
ChatModel.shared.updateChatInfo(updatedChatInfo)
|
||||
}
|
||||
}
|
||||
await onSuccess()
|
||||
} catch {
|
||||
|
@ -2497,6 +2501,9 @@ func archiveReports(_ chatInfo: ChatInfo, _ itemIds: [Int64], _ forAll: Bool, _
|
|||
ChatModel.shared.decreaseGroupReportsCounter(chatInfo.id)
|
||||
}
|
||||
}
|
||||
if let updatedChatInfo = deleted.last?.deletedChatItem.chatInfo {
|
||||
ChatModel.shared.updateChatInfo(updatedChatInfo)
|
||||
}
|
||||
}
|
||||
await onSuccess()
|
||||
} catch {
|
||||
|
|
|
@ -2533,6 +2533,11 @@ object ChatController {
|
|||
}
|
||||
}
|
||||
}
|
||||
r.chatItemDeletions.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatInfo(rhId, updatedChatInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.GroupChatItemsDeleted -> {
|
||||
groupChatItemsDeleted(rhId, r)
|
||||
|
|
|
@ -455,6 +455,7 @@ fun ChatView(
|
|||
if (deletedItem.isActiveReport) {
|
||||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
chatModel.chatsContext.updateChatInfo(chatRh, deleted.deletedChatItem.chatInfo)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (toChatItem != null) {
|
||||
|
@ -2672,6 +2673,9 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
|||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
deleted.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
|
||||
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
for (di in deleted) {
|
||||
|
@ -2712,6 +2716,9 @@ private fun archiveReports(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
|||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
deleted.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
|
||||
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
for (di in deleted) {
|
||||
|
|
|
@ -1032,8 +1032,9 @@ processChatCommand' vr = \case
|
|||
user <- getUserByGroupId db chatId
|
||||
gInfo <- getGroupInfo db vr user chatId
|
||||
pure (user, gInfo)
|
||||
chatScopeInfo <- mapM (getChatScopeInfo vr user) scope
|
||||
(timedItems, gInfo') <- withFastStore $ \db -> do
|
||||
(timedItems, gInfo') <- updateGroupChatItemsReadList db vr user gInfo scope itemIds
|
||||
(timedItems, gInfo') <- updateGroupChatItemsReadList db vr user gInfo chatScopeInfo itemIds
|
||||
timedItems' <- liftIO $ setGroupChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
|
||||
pure (timedItems', gInfo')
|
||||
forM_ timedItems $ \(itemId, deleteAt) -> startProximateTimedItemThread user (chatRef, itemId) deleteAt
|
||||
|
@ -2239,7 +2240,7 @@ processChatCommand' vr = \case
|
|||
deleted = deleted1 <> deleted2 <> deleted3 <> deleted4
|
||||
-- Read group info with updated membersRequireAttention
|
||||
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
let acis' = map (updateCIGroupInfo gInfo') acis
|
||||
let acis' = map (updateACIGroupInfo gInfo') acis
|
||||
unless (null acis') $ toView $ CEvtNewChatItems user acis'
|
||||
unless (null errs) $ toView $ CEvtChatErrors errs
|
||||
when withMessages $ deleteMessages user gInfo' deleted
|
||||
|
@ -2299,11 +2300,6 @@ processChatCommand' vr = \case
|
|||
-- instead we re-read it once after deleting all members before response.
|
||||
void $ deleteOrUpdateMemberRecordIO db user gInfo m
|
||||
pure m {memberStatus = GSMemRemoved}
|
||||
updateCIGroupInfo :: GroupInfo -> AChatItem -> AChatItem
|
||||
updateCIGroupInfo gInfo' = \case
|
||||
AChatItem SCTGroup SMDSnd (GroupChat _gInfo chatScopeInfo) ci ->
|
||||
AChatItem SCTGroup SMDSnd (GroupChat gInfo' chatScopeInfo) ci
|
||||
aci -> aci
|
||||
deleteMessages user gInfo@GroupInfo {membership} ms
|
||||
| groupFeatureMemberAllowed SGFFullDelete membership gInfo = deleteGroupMembersCIs user gInfo ms membership
|
||||
| otherwise = markGroupMembersCIsDeleted user gInfo ms membership
|
||||
|
|
|
@ -37,7 +37,7 @@ import Data.Foldable (foldr')
|
|||
import Data.Functor (($>))
|
||||
import Data.Functor.Identity
|
||||
import Data.Int (Int64)
|
||||
import Data.List (find, mapAccumL, partition)
|
||||
import Data.List (find, foldl', mapAccumL, partition)
|
||||
import Data.List.NonEmpty (NonEmpty (..), (<|))
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Data.Map.Strict (Map)
|
||||
|
@ -459,7 +459,14 @@ deleteGroupCIs user gInfo chatScopeInfo items byGroupMember_ deletedTs = do
|
|||
deleteCIFiles user ciFilesInfo
|
||||
(errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (deleteItem db) items)
|
||||
unless (null errs) $ toView $ CEvtChatErrors errs
|
||||
pure deletions
|
||||
vr <- chatVersionRange
|
||||
deletions' <- case chatScopeInfo of
|
||||
Nothing -> pure deletions
|
||||
Just scopeInfo@GCSIMemberSupport {groupMember_} -> do
|
||||
let decStats = countDeletedUnreadItems groupMember_ deletions
|
||||
gInfo' <- withFastStore' $ \db -> updateGroupScopeUnreadStats db vr user gInfo scopeInfo decStats
|
||||
pure $ map (updateDeletionGroupInfo gInfo') deletions
|
||||
pure deletions'
|
||||
where
|
||||
deleteItem :: DB.Connection -> CChatItem 'CTGroup -> IO ChatItemDeletion
|
||||
deleteItem db (CChatItem md ci) = do
|
||||
|
@ -467,6 +474,32 @@ deleteGroupCIs user gInfo chatScopeInfo items byGroupMember_ deletedTs = do
|
|||
Just m -> Just <$> updateGroupChatItemModerated db user gInfo ci m deletedTs
|
||||
Nothing -> Nothing <$ deleteGroupChatItem db user gInfo ci
|
||||
pure $ groupDeletion md gInfo chatScopeInfo ci ci'
|
||||
countDeletedUnreadItems :: Maybe GroupMember -> [ChatItemDeletion] -> (Int, Int, Int)
|
||||
countDeletedUnreadItems scopeMember_ = foldl' countItem (0, 0, 0)
|
||||
where
|
||||
countItem :: (Int, Int, Int) -> ChatItemDeletion -> (Int, Int, Int)
|
||||
countItem (!unread, !unanswered, !mentions) ChatItemDeletion {deletedChatItem}
|
||||
| aChatItemIsRcvNew deletedChatItem =
|
||||
let unread' = unread + 1
|
||||
unanswered' = case (scopeMember_, aChatItemRcvFromMember deletedChatItem) of
|
||||
(Just scopeMember, Just rcvFromMember)
|
||||
| groupMemberId' rcvFromMember == groupMemberId' scopeMember -> unanswered + 1
|
||||
_ -> unanswered
|
||||
mentions' = if isACIUserMention deletedChatItem then mentions + 1 else mentions
|
||||
in (unread', unanswered', mentions')
|
||||
| otherwise = (unread, unanswered, mentions)
|
||||
updateDeletionGroupInfo :: GroupInfo -> ChatItemDeletion -> ChatItemDeletion
|
||||
updateDeletionGroupInfo gInfo' ChatItemDeletion {deletedChatItem, toChatItem} =
|
||||
ChatItemDeletion
|
||||
{ deletedChatItem = updateACIGroupInfo gInfo' deletedChatItem,
|
||||
toChatItem = updateACIGroupInfo gInfo' <$> toChatItem
|
||||
}
|
||||
|
||||
updateACIGroupInfo :: GroupInfo -> AChatItem -> AChatItem
|
||||
updateACIGroupInfo gInfo' = \case
|
||||
AChatItem SCTGroup dir (GroupChat _gInfo chatScopeInfo) ci ->
|
||||
AChatItem SCTGroup dir (GroupChat gInfo' chatScopeInfo) ci
|
||||
aci -> aci
|
||||
|
||||
deleteGroupMemberCIs :: MsgDirectionI d => User -> GroupInfo -> GroupMember -> GroupMember -> SMsgDirection d -> CM ()
|
||||
deleteGroupMemberCIs user gInfo member byGroupMember msgDir = do
|
||||
|
|
|
@ -215,6 +215,9 @@ data CIMentionMember = CIMentionMember
|
|||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
isACIUserMention :: AChatItem -> Bool
|
||||
isACIUserMention (AChatItem _ _ _ ci) = isUserMention ci
|
||||
|
||||
isUserMention :: ChatItem c d -> Bool
|
||||
isUserMention ChatItem {meta = CIMeta {userMention}} = userMention
|
||||
|
||||
|
@ -295,6 +298,16 @@ chatItemMember GroupInfo {membership} ChatItem {chatDir} = case chatDir of
|
|||
CIGroupSnd -> membership
|
||||
CIGroupRcv m -> m
|
||||
|
||||
chatItemRcvFromMember :: ChatItem c d -> Maybe GroupMember
|
||||
chatItemRcvFromMember ChatItem {chatDir} = case chatDir of
|
||||
CIGroupRcv m -> Just m
|
||||
_ -> Nothing
|
||||
|
||||
chatItemIsRcvNew :: ChatItem c d -> Bool
|
||||
chatItemIsRcvNew ChatItem {meta = CIMeta {itemStatus}} = case itemStatus of
|
||||
CISRcvNew -> True
|
||||
_ -> False
|
||||
|
||||
ciReactionAllowed :: ChatItem c d -> Bool
|
||||
ciReactionAllowed ChatItem {meta = CIMeta {itemDeleted = Just _}} = False
|
||||
ciReactionAllowed ChatItem {content} = isJust $ ciMsgContent content
|
||||
|
@ -395,6 +408,12 @@ aChatItemTs (AChatItem _ _ _ ci) = chatItemTs' ci
|
|||
aChatItemDir :: AChatItem -> MsgDirection
|
||||
aChatItemDir (AChatItem _ sMsgDir _ _) = toMsgDirection sMsgDir
|
||||
|
||||
aChatItemRcvFromMember :: AChatItem -> Maybe GroupMember
|
||||
aChatItemRcvFromMember (AChatItem _ _ _ ci) = chatItemRcvFromMember ci
|
||||
|
||||
aChatItemIsRcvNew :: AChatItem -> Bool
|
||||
aChatItemIsRcvNew (AChatItem _ _ _ ci) = chatItemIsRcvNew ci
|
||||
|
||||
updateFileStatus :: forall c d. ChatItem c d -> CIFileStatus d -> ChatItem c d
|
||||
updateFileStatus ci@ChatItem {file} status = case file of
|
||||
Just f -> ci {file = Just (f :: CIFile d) {fileStatus = status}}
|
||||
|
@ -966,7 +985,10 @@ ciStatusNew = case msgDirection @d of
|
|||
ciCreateStatus :: forall d. MsgDirectionI d => CIContent d -> CIStatus d
|
||||
ciCreateStatus content = case msgDirection @d of
|
||||
SMDSnd -> ciStatusNew
|
||||
SMDRcv -> if ciRequiresAttention content then ciStatusNew else CISRcvRead
|
||||
SMDRcv
|
||||
| isCIReport content -> CISRcvRead
|
||||
| ciRequiresAttention content -> ciStatusNew
|
||||
| otherwise -> CISRcvRead
|
||||
|
||||
membersGroupItemStatus :: [(GroupSndStatus, Int)] -> CIStatus 'MDSnd
|
||||
membersGroupItemStatus memStatusCounts
|
||||
|
|
|
@ -182,6 +182,9 @@ ciMsgContent = \case
|
|||
CIRcvMsgContent mc -> Just mc
|
||||
_ -> Nothing
|
||||
|
||||
isCIReport :: CIContent d -> Bool
|
||||
isCIReport = maybe False isReport . ciMsgContent
|
||||
|
||||
data MsgDecryptError
|
||||
= MDERatchetHeader
|
||||
| MDETooManySkipped
|
||||
|
|
|
@ -1233,7 +1233,7 @@ updateGroupMemberAccepted db User {userId} m@GroupMember {groupMemberId} status
|
|||
pure m {memberStatus = status, memberRole = role, updatedAt = currentTs}
|
||||
|
||||
deleteGroupMemberSupportChat :: DB.Connection -> User -> GroupInfo -> GroupMember -> IO (GroupInfo, GroupMember)
|
||||
deleteGroupMemberSupportChat db user@User {userId} g@GroupInfo {groupId, membersRequireAttention} m@GroupMember {groupMemberId, supportChat} = do
|
||||
deleteGroupMemberSupportChat db user g m@GroupMember {groupMemberId} = do
|
||||
let requiredAttention = gmRequiresAttention m
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
|
|
|
@ -80,6 +80,7 @@ module Simplex.Chat.Store.Messages
|
|||
updateGroupChatItemsRead,
|
||||
getGroupUnreadTimedItems,
|
||||
updateGroupChatItemsReadList,
|
||||
updateGroupScopeUnreadStats,
|
||||
setGroupChatItemsDeleteAt,
|
||||
updateLocalChatItemsRead,
|
||||
getChatRefViaItemId,
|
||||
|
@ -2018,13 +2019,17 @@ getGroupUnreadTimedItems db User {userId} groupId =
|
|||
|]
|
||||
(userId, groupId, CISRcvNew)
|
||||
|
||||
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
|
||||
updateGroupChatItemsReadList :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> NonEmpty ChatItemId -> ExceptT StoreError IO ([(ChatItemId, Int)], GroupInfo)
|
||||
updateGroupChatItemsReadList db vr user@User {userId} g@GroupInfo {groupId} scopeInfo_ 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 <- liftIO $ catMaybes . L.toList <$> mapM (getUpdateGroupItem currentTs) itemIds
|
||||
g' <- updateChatStats readItemsData
|
||||
g' <- case scopeInfo_ of
|
||||
Nothing -> pure g
|
||||
Just scopeInfo@GCSIMemberSupport {groupMember_} -> do
|
||||
let decStats = countReadItems groupMember_ readItemsData
|
||||
liftIO $ updateGroupScopeUnreadStats db vr user g scopeInfo decStats
|
||||
pure (timedItems readItemsData, g')
|
||||
where
|
||||
getUpdateGroupItem :: UTCTime -> ChatItemId -> IO (Maybe (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt))
|
||||
|
@ -2038,67 +2043,58 @@ updateGroupChatItemsReadList db vr user@User {userId} g@GroupInfo {groupId, memb
|
|||
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)] -> ExceptT StoreError IO GroupInfo
|
||||
updateChatStats readItemsData = case scope of
|
||||
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
|
||||
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
|
||||
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')
|
||||
countReadItems :: Maybe GroupMember -> [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> (Int, Int, Int)
|
||||
countReadItems scopeMember_ readItemsData =
|
||||
let unread = length readItemsData
|
||||
(unanswered, mentions) = foldl' countItem (0, 0) readItemsData
|
||||
in (unread, unanswered, mentions)
|
||||
where
|
||||
countItem :: (Int, Int) -> (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt) -> (Int, Int)
|
||||
countItem (!unanswered, !mentions) (_, _, _, itemGMId_, userMention_) =
|
||||
let unanswered' = case (scopeMember_, itemGMId_) of
|
||||
(Just scopeMember, Just itemGMId) | itemGMId == groupMemberId' scopeMember -> 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
|
||||
addTimedItem acc (itemId, Just ttl, Nothing, _, _) = (itemId, ttl) : acc
|
||||
addTimedItem acc _ = acc
|
||||
|
||||
updateGroupScopeUnreadStats :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> GroupChatScopeInfo -> (Int, Int, Int) -> IO GroupInfo
|
||||
updateGroupScopeUnreadStats db vr user g@GroupInfo {membership} scopeInfo (unread, unanswered, mentions) =
|
||||
case scopeInfo of
|
||||
GCSIMemberSupport {groupMember_} -> case groupMember_ of
|
||||
Nothing -> do
|
||||
membership' <- updateGMStats membership
|
||||
pure g {membership = membership'}
|
||||
Just member -> do
|
||||
member' <- updateGMStats member
|
||||
let didRequire = gmRequiresAttention member
|
||||
nowRequires = gmRequiresAttention member'
|
||||
if (not nowRequires && didRequire)
|
||||
then decreaseGroupMembersRequireAttention db user g
|
||||
else pure g
|
||||
where
|
||||
updateGMStats m@GroupMember {groupMemberId} = do
|
||||
currentTs <- getCurrentTime
|
||||
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 - ?,
|
||||
updated_at = ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(unread, unanswered, mentions, currentTs, groupMemberId)
|
||||
m_ <- runExceptT $ getGroupMemberById db vr user groupMemberId
|
||||
pure $ either (const m) id m_ -- Left shouldn't happen, but types require it
|
||||
|
||||
deriving instance Show BoolInt
|
||||
|
||||
setGroupChatItemsDeleteAt :: DB.Connection -> User -> GroupId -> [(ChatItemId, Int)] -> UTCTime -> IO [(ChatItemId, UTCTime)]
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
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 = ?,
|
||||
|
@ -1321,6 +1303,17 @@ Query:
|
|||
Plan:
|
||||
SEARCH group_members 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 - ?,
|
||||
updated_at = ?
|
||||
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 = ?
|
||||
|
|
|
@ -196,7 +196,8 @@ chatGroupTests = do
|
|||
it "should send scoped messages to support (single moderator)" testScopedSupportSingleModerator
|
||||
it "should send scoped messages to support (many moderators)" testScopedSupportManyModerators
|
||||
it "should send messages to admins and members" testSupportCLISendCommand
|
||||
it "should correctly maintain unread stats for support chats" testScopedSupportUnreadStats
|
||||
it "should correctly maintain unread stats for support chats on reading chat items" testScopedSupportUnreadStatsOnRead
|
||||
it "should correctly maintain unread stats for support chats on deleting chat items" testScopedSupportUnreadStatsOnDelete
|
||||
|
||||
testGroupCheckMessages :: HasCallStack => TestParams -> IO ()
|
||||
testGroupCheckMessages =
|
||||
|
@ -7009,8 +7010,8 @@ testSupportCLISendCommand =
|
|||
bob ##> "#team (support 4"
|
||||
bob <## "bad chat command: Failed reading: empty"
|
||||
|
||||
testScopedSupportUnreadStats :: HasCallStack => TestParams -> IO ()
|
||||
testScopedSupportUnreadStats =
|
||||
testScopedSupportUnreadStatsOnRead :: HasCallStack => TestParams -> IO ()
|
||||
testScopedSupportUnreadStatsOnRead =
|
||||
testChatOpts4 opts aliceProfile bobProfile cathProfile danProfile $ \alice bob cath dan -> do
|
||||
createGroup4 "team" alice (bob, GRMember) (cath, GRMember) (dan, GRModerator)
|
||||
|
||||
|
@ -7149,3 +7150,33 @@ testScopedSupportUnreadStats =
|
|||
{ markRead = False
|
||||
}
|
||||
|
||||
testScopedSupportUnreadStatsOnDelete :: HasCallStack => TestParams -> IO ()
|
||||
testScopedSupportUnreadStatsOnDelete =
|
||||
testChatOpts2 opts aliceProfile bobProfile $ \alice bob -> do
|
||||
createGroup2 "team" alice bob
|
||||
|
||||
alice ##> "/set delete #team on"
|
||||
alice <## "updated group preferences:"
|
||||
alice <## "Full deletion: on"
|
||||
bob <## "alice updated group #team:"
|
||||
bob <## "updated group preferences:"
|
||||
bob <## "Full deletion: on"
|
||||
|
||||
bob #> "#team (support) 1"
|
||||
alice <# "#team (support: bob) bob> 1"
|
||||
|
||||
msgIdBob <- lastItemId bob
|
||||
|
||||
alice ##> "/member support chats #team"
|
||||
alice <## "bob (Bob) (id 2): unread: 1, require attention: 1, mentions: 0"
|
||||
|
||||
bob #$> ("/_delete item #1(_support) " <> msgIdBob <> " broadcast", id, "message deleted")
|
||||
alice <# "#team (support: bob) bob> [deleted] 1"
|
||||
|
||||
alice ##> "/member support chats #team"
|
||||
alice <## "bob (Bob) (id 2): unread: 0, require attention: 0, mentions: 0"
|
||||
where
|
||||
opts =
|
||||
testOpts
|
||||
{ markRead = False
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue