ee636f1994
For watchers and users with pins that don't have access to the private board where the card was moved to.
143 lines
4.4 KiB
Ruby
143 lines
4.4 KiB
Ruby
class Card < ApplicationRecord
|
|
include Accessible, Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, Eventable,
|
|
Exportable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable,
|
|
Readable, Searchable, Stallable, Statuses, Storage::Tracked, Taggable, Triageable, Watchable
|
|
|
|
belongs_to :account, default: -> { board.account }
|
|
belongs_to :board
|
|
belongs_to :creator, class_name: "User", default: -> { Current.user }
|
|
|
|
has_many :comments, dependent: :destroy
|
|
has_one_attached :image, dependent: :purge_later
|
|
|
|
has_rich_text :description
|
|
|
|
before_save :set_default_title, if: :published?
|
|
before_create :assign_number
|
|
|
|
after_save -> { board.touch }, if: :published?
|
|
after_touch -> { board.touch }, if: :published?
|
|
after_update :handle_board_change, if: :saved_change_to_board_id?
|
|
|
|
scope :reverse_chronologically, -> { order created_at: :desc, id: :desc }
|
|
scope :chronologically, -> { order created_at: :asc, id: :asc }
|
|
scope :latest, -> { order last_active_at: :desc, id: :desc }
|
|
scope :with_users, -> { preload(creator: [ :avatar_attachment, :account ], assignees: [ :avatar_attachment, :account ]) }
|
|
scope :preloaded, -> { with_users.preload(:column, :tags, :steps, :closure, :goldness, :activity_spike, :image_attachment, board: [ :entropy, :columns ], not_now: [ :user ]).with_rich_text_description_and_embeds }
|
|
|
|
scope :indexed_by, ->(index) do
|
|
case index
|
|
when "stalled" then stalled
|
|
when "postponing_soon" then postponing_soon
|
|
when "closed" then closed
|
|
when "not_now" then postponed.latest
|
|
when "golden" then golden
|
|
when "draft" then drafted
|
|
else all
|
|
end
|
|
end
|
|
|
|
scope :sorted_by, ->(sort) do
|
|
case sort
|
|
when "newest" then reverse_chronologically
|
|
when "oldest" then chronologically
|
|
when "latest" then latest
|
|
else latest
|
|
end
|
|
end
|
|
|
|
def card
|
|
self
|
|
end
|
|
|
|
def to_param
|
|
number.to_s
|
|
end
|
|
|
|
def move_to(new_board)
|
|
transaction do
|
|
card.update!(board: new_board)
|
|
card.events.update_all(board_id: new_board.id)
|
|
end
|
|
end
|
|
|
|
def filled?
|
|
title.present? || description.present?
|
|
end
|
|
|
|
private
|
|
STORAGE_BATCH_SIZE = 1000
|
|
|
|
# Override to include comments, but only load comments that have attachments.
|
|
# Cards can have thousands of comments; most won't have attachments.
|
|
# Streams in batches to avoid loading all IDs into memory at once.
|
|
def storage_transfer_records
|
|
comment_ids_with_attachments = storage_comment_ids_with_attachments
|
|
|
|
if comment_ids_with_attachments.any?
|
|
[ self, *comments.where(id: comment_ids_with_attachments).to_a ]
|
|
else
|
|
[ self ]
|
|
end
|
|
end
|
|
|
|
def storage_comment_ids_with_attachments
|
|
direct = []
|
|
rich_text_map = {}
|
|
|
|
# Stream comment IDs in batches to avoid loading all into memory
|
|
comments.in_batches(of: STORAGE_BATCH_SIZE) do |batch|
|
|
batch_ids = batch.pluck(:id)
|
|
|
|
direct.concat \
|
|
ActiveStorage::Attachment
|
|
.where(record_type: "Comment", record_id: batch_ids)
|
|
.distinct
|
|
.pluck(:record_id)
|
|
|
|
ActionText::RichText
|
|
.where(record_type: "Comment", record_id: batch_ids)
|
|
.pluck(:id, :record_id)
|
|
.each { |rt_id, comment_id| rich_text_map[rt_id] = comment_id }
|
|
end
|
|
|
|
embed_comment_ids = if rich_text_map.any?
|
|
rich_text_map.keys.each_slice(STORAGE_BATCH_SIZE).flat_map do |batch_ids|
|
|
ActiveStorage::Attachment
|
|
.where(record_type: "ActionText::RichText", record_id: batch_ids)
|
|
.distinct
|
|
.pluck(:record_id)
|
|
end.filter_map { |rt_id| rich_text_map[rt_id] }
|
|
else
|
|
[]
|
|
end
|
|
|
|
(direct + embed_comment_ids).uniq
|
|
end
|
|
|
|
def set_default_title
|
|
self.title = "Untitled" if title.blank?
|
|
end
|
|
|
|
def handle_board_change
|
|
old_board = account.boards.find_by(id: board_id_before_last_save)
|
|
|
|
transaction do
|
|
update! column: nil
|
|
track_board_change_event(old_board.name)
|
|
grant_access_to_assignees unless board.all_access?
|
|
end
|
|
|
|
remove_inaccessible_notifications_later
|
|
clean_inaccessible_data_later
|
|
end
|
|
|
|
def track_board_change_event(old_board_name)
|
|
track_event "board_changed", particulars: { old_board: old_board_name, new_board: board.name }
|
|
end
|
|
|
|
def assign_number
|
|
self.number ||= account.increment!(:cards_count).cards_count
|
|
end
|
|
end
|