911cc14481
* Add follow_request notification type The notification type already existed in the backend but was never pushed to the front-end. This also means translation strings were also available for the backend, from the notification mailer. Unlike other notification types, these are off by default, to match what I remember of Gargron's view on the topic: that follow requests should not clutter notifications and should instead be reviewed at the user's own leisure in the dedicated column. Since follow requests have their own column, I've deemed it unnecessary to add a specific tab for them in the notification quick filter. * Show follow request link in single-column if there are pending requests, even if account isn't locked * Push follow requests from notifications to the follow_requests list * Offer to accept or reject follow request from the notification * Redesign follow request notification
110 lines
3.7 KiB
Ruby
110 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
|
# == Schema Information
|
|
#
|
|
# Table name: notifications
|
|
#
|
|
# id :bigint(8) not null, primary key
|
|
# activity_id :bigint(8) not null
|
|
# activity_type :string not null
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# account_id :bigint(8) not null
|
|
# from_account_id :bigint(8) not null
|
|
#
|
|
|
|
class Notification < ApplicationRecord
|
|
include Paginable
|
|
include Cacheable
|
|
|
|
TYPE_CLASS_MAP = {
|
|
mention: 'Mention',
|
|
reblog: 'Status',
|
|
follow: 'Follow',
|
|
follow_request: 'FollowRequest',
|
|
favourite: 'Favourite',
|
|
poll: 'Poll',
|
|
}.freeze
|
|
|
|
STATUS_INCLUDES = [:account, :application, :preloadable_poll, :media_attachments, :tags, active_mentions: :account, reblog: [:account, :application, :preloadable_poll, :media_attachments, :tags, active_mentions: :account]].freeze
|
|
|
|
belongs_to :account, optional: true
|
|
belongs_to :from_account, class_name: 'Account', optional: true
|
|
belongs_to :activity, polymorphic: true, optional: true
|
|
|
|
belongs_to :mention, foreign_type: 'Mention', foreign_key: 'activity_id', optional: true
|
|
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', optional: true
|
|
belongs_to :follow, foreign_type: 'Follow', foreign_key: 'activity_id', optional: true
|
|
belongs_to :follow_request, foreign_type: 'FollowRequest', foreign_key: 'activity_id', optional: true
|
|
belongs_to :favourite, foreign_type: 'Favourite', foreign_key: 'activity_id', optional: true
|
|
belongs_to :poll, foreign_type: 'Poll', foreign_key: 'activity_id', optional: true
|
|
|
|
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
|
validates :activity_type, inclusion: { in: TYPE_CLASS_MAP.values }
|
|
|
|
scope :browserable, ->(exclude_types = [], account_id = nil) {
|
|
types = TYPE_CLASS_MAP.values - activity_types_from_types(exclude_types)
|
|
if account_id.nil?
|
|
where(activity_type: types)
|
|
else
|
|
where(activity_type: types, from_account_id: account_id)
|
|
end
|
|
}
|
|
|
|
cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account, follow_request: :account, poll: [status: STATUS_INCLUDES]
|
|
|
|
def type
|
|
@type ||= TYPE_CLASS_MAP.invert[activity_type].to_sym
|
|
end
|
|
|
|
def target_status
|
|
case type
|
|
when :reblog
|
|
status&.reblog
|
|
when :favourite
|
|
favourite&.status
|
|
when :mention
|
|
mention&.status
|
|
when :poll
|
|
poll&.status
|
|
end
|
|
end
|
|
|
|
class << self
|
|
def cache_ids
|
|
select(:id, :updated_at, :activity_type, :activity_id)
|
|
end
|
|
|
|
def reload_stale_associations!(cached_items)
|
|
account_ids = (cached_items.map(&:from_account_id) + cached_items.map { |item| item.target_status&.account_id }.compact).uniq
|
|
|
|
return if account_ids.empty?
|
|
|
|
accounts = Account.where(id: account_ids).includes(:account_stat).each_with_object({}) { |a, h| h[a.id] = a }
|
|
|
|
cached_items.each do |item|
|
|
item.from_account = accounts[item.from_account_id]
|
|
item.target_status.account = accounts[item.target_status.account_id] if item.target_status
|
|
end
|
|
end
|
|
|
|
def activity_types_from_types(types)
|
|
types.map { |type| TYPE_CLASS_MAP[type.to_sym] }.compact
|
|
end
|
|
end
|
|
|
|
after_initialize :set_from_account
|
|
before_validation :set_from_account
|
|
|
|
private
|
|
|
|
def set_from_account
|
|
return unless new_record?
|
|
|
|
case activity_type
|
|
when 'Status', 'Follow', 'Favourite', 'FollowRequest', 'Poll'
|
|
self.from_account_id = activity&.account_id
|
|
when 'Mention'
|
|
self.from_account_id = activity&.status&.account_id
|
|
end
|
|
end
|
|
end
|