mirror of
https://github.com/discourse/discourse.git
synced 2025-06-05 08:37:19 +08:00
FIX: Incorrect unread count shown in channel when message deleted (#21410)
When we were deleting messages in chat, we would find all of the UserChatChannelMembership records that had a matching last_read_message_id and set that column to NULL. This became an issue when multiple users had that deleted message set to their last_read_message_id. When we called ChannelUnreadsQuery to get the unread count for each of the user's channels, we were COALESCing the last_read_message_id and returning 0 if it was NULL, which meant that the unread count for the channel would be the total count of the messages not sent by the user in that channel. This was particularly noticeable for DM channels since we show the count with the indicator in the header. This issue would disappear as soon as the user opened the problem channel, because we would then set the last_read_message_id to an actual ID. To circumvent this, instead of NULLifying the last_read_message_id in most cases, it makes more sense to just set it to the most recent non-deleted chat message ID for the channel. The only time it will be set to NULL now is when there are no more other messages in the channel.
This commit is contained in:
@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Chat
|
||||
module Action
|
||||
class ResetUserLastReadChannelMessage
|
||||
# @param [Array] last_read_message_ids The message IDs to match with the
|
||||
# last_read_message_ids in UserChannelMembership which will be reset
|
||||
# to NULL or the most recent non-deleted message in the channel to
|
||||
# update read state.
|
||||
# @param [Integer] channel_ids The channel IDs of the memberships to update,
|
||||
# this is used to find the latest non-deleted message in the channel.
|
||||
def self.call(last_read_message_ids, channel_ids)
|
||||
sql = <<~SQL
|
||||
-- update the last_read_message_id to the most recent
|
||||
-- non-deleted message in the channel so unread counts are correct.
|
||||
-- the cte row_number is necessary to only return a single row
|
||||
-- for each channel to prevent additional data being returned
|
||||
WITH cte AS (
|
||||
SELECT * FROM (
|
||||
SELECT id, chat_channel_id, row_number() OVER (
|
||||
PARTITION BY chat_channel_id ORDER BY created_at DESC, id DESC
|
||||
) AS row_number
|
||||
FROM chat_messages
|
||||
WHERE deleted_at IS NULL AND chat_channel_id IN (:channel_ids)
|
||||
) AS recent_messages
|
||||
WHERE recent_messages.row_number = 1
|
||||
)
|
||||
UPDATE user_chat_channel_memberships
|
||||
SET last_read_message_id = cte.id
|
||||
FROM cte
|
||||
WHERE user_chat_channel_memberships.last_read_message_id IN (:last_read_message_ids)
|
||||
AND cte.chat_channel_id = user_chat_channel_memberships.chat_channel_id;
|
||||
|
||||
-- then reset all last_read_message_ids to null
|
||||
-- for the cases where all messages in the channel were
|
||||
-- already deleted
|
||||
UPDATE user_chat_channel_memberships
|
||||
SET last_read_message_id = NULL
|
||||
WHERE last_read_message_id IN (:last_read_message_ids);
|
||||
SQL
|
||||
|
||||
DB.exec(sql, last_read_message_ids: last_read_message_ids, channel_ids: channel_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -6,17 +6,20 @@ module Chat
|
||||
chat_messages_query
|
||||
.in_batches(of: batch_size)
|
||||
.each do |relation|
|
||||
destroyed_ids = relation.destroy_all.pluck(:id)
|
||||
reset_last_read(destroyed_ids)
|
||||
delete_flags(destroyed_ids)
|
||||
destroyed_ids = relation.destroy_all.pluck(:id, :chat_channel_id)
|
||||
destroyed_message_ids = destroyed_ids.map(&:first).uniq
|
||||
destroyed_message_channel_ids = destroyed_ids.map(&:second).uniq
|
||||
reset_last_read(destroyed_message_ids, destroyed_message_channel_ids)
|
||||
delete_flags(destroyed_message_ids)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reset_last_read(message_ids)
|
||||
Chat::UserChatChannelMembership.where(last_read_message_id: message_ids).update_all(
|
||||
last_read_message_id: nil,
|
||||
def reset_last_read(destroyed_message_ids, destroyed_message_channel_ids)
|
||||
::Chat::Action::ResetUserLastReadChannelMessage.call(
|
||||
destroyed_message_ids,
|
||||
destroyed_message_channel_ids,
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -58,9 +58,7 @@ module Chat
|
||||
end
|
||||
|
||||
def update_tracking_state(message:, **)
|
||||
Chat::UserChatChannelMembership.where(last_read_message_id: message.id).update_all(
|
||||
last_read_message_id: nil,
|
||||
)
|
||||
::Chat::Action::ResetUserLastReadChannelMessage.call([message.id], [message.chat_channel_id])
|
||||
end
|
||||
|
||||
def update_thread_reply_cache(message:, **)
|
||||
|
Reference in New Issue
Block a user