mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-09 08:10:08 +09:00
7331d3a946
Apply the Laravel implementation’s MessageWriter architecture to Rails creation: save native Action Text and attachment metadata, insert the search entry and update unread memberships in the message transaction. Keep notification jobs after commit and preserve Room#receive and existing serialized jobs. Cover native HTML/mentions, attachment-only search, derived-write failures, outer rollback and SQL commit ordering. Updates and destruction retain their current indexing behavior.
57 lines
1.8 KiB
Ruby
57 lines
1.8 KiB
Ruby
class Message < ApplicationRecord
|
|
include Attachment, Broadcasts, Mentionee, Pagination, Searchable
|
|
|
|
belongs_to :room, touch: true
|
|
belongs_to :creator, class_name: "User", default: -> { Current.user }
|
|
|
|
has_many :boosts, dependent: :destroy
|
|
|
|
has_rich_text :body
|
|
|
|
before_create -> { self.client_message_id ||= Random.uuid } # Bots don't care
|
|
# Run after Action Text and Active Storage autosave, while creation is still atomic.
|
|
after_save :record_creation, if: :previously_new_record?
|
|
after_create_commit -> { room.push_later(self) }
|
|
|
|
scope :ordered, -> { order(:created_at) }
|
|
scope :with_creator, -> { preload(creator: :avatar_attachment) }
|
|
scope :with_attachment_details, -> {
|
|
with_rich_text_body_and_embeds.with_attached_attachment
|
|
.includes(attachment_blob: :variant_records)
|
|
}
|
|
scope :with_boosts, -> { includes(boosts: { booster: :avatar_attachment }) }
|
|
scope :with_presentation, -> { with_creator.with_attachment_details.with_boosts.preload(:room) }
|
|
|
|
def plain_text_body
|
|
content = body.body
|
|
text = if content && content.fragment.find_all(ActionText::Attachment.tag_name).empty?
|
|
# ActionText::Content#to_plain_text duplicates the fragment to replace
|
|
# attachments. Without them, reuse its native Fragment conversion instead.
|
|
content.fragment.to_plain_text.dup
|
|
else
|
|
body.to_plain_text
|
|
end
|
|
text.presence || attachment&.filename&.to_s || ""
|
|
end
|
|
|
|
def content_type
|
|
case
|
|
when attachment? then "attachment"
|
|
when sound.present? then "sound"
|
|
else "text"
|
|
end.inquiry
|
|
end
|
|
|
|
def sound
|
|
plain_text_body.match(/\A\/play (?<name>\w+)\z/) do |match|
|
|
Sound.find_by_name match[:name]
|
|
end
|
|
end
|
|
|
|
private
|
|
def record_creation
|
|
create_in_index
|
|
room.unread_memberships(self)
|
|
end
|
|
end
|