FIX: Trashing message should reset last read (#20912)

When a chat message is trashed and the message is used
for someone's UserChatChannelMembership#last_read_message_id,
the user would end up with some read state issues until
someone posted a new message in the channel, since we didn't
clear it like we did on bulk message delete.

This commit fixes the issue, and also takes the opportunity
to start a MessagesController in the API namespace, and move
the trash message functionality into the new service format.
This commit is contained in:
Martin Brennan
2023-04-04 09:30:38 +10:00
committed by GitHub
parent 3b28d03780
commit 894586afa9
11 changed files with 299 additions and 170 deletions

View File

@@ -12,19 +12,6 @@ module Chat
end
end
def trash_message(message, actor)
Chat::Message.transaction do
message.trash!(actor)
Chat::Mention.where(chat_message: message).destroy_all
DiscourseEvent.trigger(:chat_message_trashed, message, message.chat_channel, actor)
# FIXME: We should do something to prevent the blue/green bubble
# of other channel members from getting out of sync when a message
# gets deleted.
Chat::Publisher.publish_delete!(message.chat_channel, message)
end
end
private
def reset_last_read(message_ids)

View File

@@ -0,0 +1,70 @@
# frozen_string_literal: true
module Chat
# Service responsible for trashing a chat message
# for a channel and ensuring that the client and read state is
# updated.
#
# @example
# Chat::TrashMessage.call(message_id: 2, channel_id: 1, guardian: guardian)
#
class TrashMessage
include Service::Base
# @!method call(message_id:, channel_id:, guardian:)
# @param [Integer] message_id
# @param [Integer] channel_id
# @param [Guardian] guardian
# @return [Service::Base::Context]
contract
model :message
policy :invalid_access
transaction do
step :trash_message
step :destroy_mentions
step :update_tracking_state
end
step :publish_events
# @!visibility private
class Contract
attribute :message_id, :integer
attribute :channel_id, :integer
validates :message_id, presence: true
validates :channel_id, presence: true
end
private
def fetch_message(contract:, **)
Chat::Message.includes(chat_channel: :chatable).find_by(
id: contract.message_id,
chat_channel_id: contract.channel_id,
)
end
def invalid_access(guardian:, message:, **)
guardian.can_delete_chat?(message, message.chat_channel.chatable)
end
def trash_message(message:, **)
message.trash!
end
def destroy_mentions(message:, **)
Chat::Mention.where(chat_message: message).destroy_all
end
def update_tracking_state(message:, **)
Chat::UserChatChannelMembership.where(last_read_message_id: message.id).update_all(
last_read_message_id: nil,
)
end
def publish_events(guardian:, message:, **)
DiscourseEvent.trigger(:chat_message_trashed, message, message.chat_channel, guardian.user)
Chat::Publisher.publish_delete!(message.chat_channel, message)
end
end
end