Add status references support
This commit is contained in:
parent
af161fa66b
commit
22ad776635
24 changed files with 321 additions and 7 deletions
|
@ -109,6 +109,7 @@ Metrics/ParameterLists:
|
|||
Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
- 'app/policies/status_policy.rb'
|
||||
- 'app/services/post_status_service.rb'
|
||||
|
||||
# Reason: Prevailing style is argument file paths
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
|
||||
|
|
|
@ -143,6 +143,7 @@ const excludeTypesFromFilter = filter => {
|
|||
'favourite',
|
||||
'emoji_reaction',
|
||||
'reblog',
|
||||
'status_reference',
|
||||
'mention',
|
||||
'poll',
|
||||
'status',
|
||||
|
|
|
@ -152,6 +152,17 @@ export default class ColumnSettings extends PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-status_reference'>
|
||||
<span id='notifications-status_reference' className='column-settings__section'><FormattedMessage id='notifications.column_settings.status_reference' defaultMessage='References:' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'status_reference']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'status_reference']} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'status_reference']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'status_reference']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-poll'>
|
||||
<span id='notifications-poll' className='column-settings__section'><FormattedMessage id='notifications.column_settings.poll' defaultMessage='Poll results:' /></span>
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ const tooltips = defineMessages({
|
|||
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' },
|
||||
emojiReactions: { id: 'notifications.filter.emoji_reactions', defaultMessage: 'Stamps' },
|
||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||
status_references: { id: 'notifications.filter.status_references', defaultMessage: 'Status references' },
|
||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||
statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
|
||||
|
@ -90,6 +91,13 @@ class FilterBar extends PureComponent {
|
|||
>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
</button>
|
||||
<button
|
||||
className={selectedFilter === 'status_reference' ? 'active' : ''}
|
||||
onClick={this.onClick('status_reference')}
|
||||
title={intl.formatMessage(tooltips.status_references)}
|
||||
>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
</button>
|
||||
<button
|
||||
className={selectedFilter === 'poll' ? 'active' : ''}
|
||||
onClick={this.onClick('poll')}
|
||||
|
|
|
@ -28,6 +28,7 @@ const messages = defineMessages({
|
|||
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||
reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
||||
status: { id: 'notification.status', defaultMessage: '{name} just posted' },
|
||||
statusReference: { id: 'notification.status_reference', defaultMessage: '{name} refered' },
|
||||
update: { id: 'notification.update', defaultMessage: '{name} edited a post' },
|
||||
adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
|
||||
adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
|
||||
|
@ -288,6 +289,39 @@ class Notification extends ImmutablePureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
renderStatusReference (notification, link) {
|
||||
const { intl, unread } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className={classNames('notification notification-reblog focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
</div>
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.status_reference' defaultMessage='{name} referenced your status' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
muted
|
||||
withDismiss
|
||||
hidden={this.props.hidden}
|
||||
getScrollPosition={this.props.getScrollPosition}
|
||||
updateScrollBottom={this.props.updateScrollBottom}
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
withoutEmojiReactions
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
}
|
||||
|
||||
renderStatus (notification, link) {
|
||||
const { intl, unread, status } = this.props;
|
||||
|
||||
|
@ -479,6 +513,8 @@ class Notification extends ImmutablePureComponent {
|
|||
return this.renderEmojiReaction(notification, link);
|
||||
case 'reblog':
|
||||
return this.renderReblog(notification, link);
|
||||
case 'status_reference':
|
||||
return this.renderStatusReference(notification, link);
|
||||
case 'status':
|
||||
return this.renderStatus(notification, link);
|
||||
case 'update':
|
||||
|
|
|
@ -415,6 +415,7 @@
|
|||
"notification.poll": "アンケートが終了しました",
|
||||
"notification.reblog": "{name}さんがあなたの投稿をブーストしました",
|
||||
"notification.status": "{name}さんが投稿しました",
|
||||
"notification.status_reference": "{name}さんがあなたの投稿を参照しました",
|
||||
"notification.update": "{name}さんが投稿を編集しました",
|
||||
"notifications.clear": "通知を消去",
|
||||
"notifications.clear_confirmation": "本当に通知を消去しますか?",
|
||||
|
|
|
@ -97,6 +97,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
fetch_replies(@status)
|
||||
distribute
|
||||
forward_for_reply
|
||||
process_references!
|
||||
join_group!
|
||||
end
|
||||
|
||||
|
@ -471,6 +472,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
retry
|
||||
end
|
||||
|
||||
def process_references!
|
||||
references = []
|
||||
references = ActivityPub::FetchReferencesService(@json['references']) unless @json['references'].nil?
|
||||
ProcessReferencesWorker.perform_async(@status.id, [], urls: references)
|
||||
end
|
||||
|
||||
def join_group!
|
||||
GroupReblogService.new.call(@status)
|
||||
end
|
||||
|
|
|
@ -26,6 +26,7 @@ class Notification < ApplicationRecord
|
|||
'FollowRequest' => :follow_request,
|
||||
'Favourite' => :favourite,
|
||||
'EmojiReaction' => :emoji_reaction,
|
||||
'StatusReference' => :status_reference,
|
||||
'Poll' => :poll,
|
||||
}.freeze
|
||||
|
||||
|
@ -33,6 +34,7 @@ class Notification < ApplicationRecord
|
|||
mention
|
||||
status
|
||||
reblog
|
||||
status_reference
|
||||
follow
|
||||
follow_request
|
||||
favourite
|
||||
|
@ -47,6 +49,7 @@ class Notification < ApplicationRecord
|
|||
TARGET_STATUS_INCLUDES_BY_TYPE = {
|
||||
status: :status,
|
||||
reblog: [status: :reblog],
|
||||
status_reference: [status_reference: :status],
|
||||
mention: [mention: :status],
|
||||
favourite: [favourite: :status],
|
||||
emoji_reaction: [emoji_reaction: :status],
|
||||
|
@ -67,6 +70,7 @@ class Notification < ApplicationRecord
|
|||
belongs_to :follow_request, inverse_of: :notification
|
||||
belongs_to :favourite, inverse_of: :notification
|
||||
belongs_to :emoji_reaction, inverse_of: :notification
|
||||
belongs_to :status_reference, inverse_of: :notification
|
||||
belongs_to :poll, inverse_of: false
|
||||
belongs_to :report, inverse_of: false
|
||||
end
|
||||
|
@ -85,6 +89,8 @@ class Notification < ApplicationRecord
|
|||
status
|
||||
when :reblog
|
||||
status&.reblog
|
||||
when :status_reference
|
||||
status_reference&.status
|
||||
when :favourite
|
||||
favourite&.status
|
||||
when :emoji_reaction, :reaction
|
||||
|
@ -136,6 +142,8 @@ class Notification < ApplicationRecord
|
|||
notification.status = cached_status
|
||||
when :reblog
|
||||
notification.status.reblog = cached_status
|
||||
when :status_reference
|
||||
notification.status_reference.status = cached_status
|
||||
when :favourite
|
||||
notification.favourite.status = cached_status
|
||||
when :emoji_reaction, :reaction
|
||||
|
@ -162,7 +170,7 @@ class Notification < ApplicationRecord
|
|||
case activity_type
|
||||
when 'Status', 'Follow', 'Favourite', 'EmojiReaction', 'EmojiReact', 'FollowRequest', 'Poll', 'Report'
|
||||
self.from_account_id = activity&.account_id
|
||||
when 'Mention'
|
||||
when 'Mention', 'StatusReference'
|
||||
self.from_account_id = activity&.status&.account_id
|
||||
when 'Account'
|
||||
self.from_account_id = activity&.id
|
||||
|
|
|
@ -75,6 +75,10 @@ class Status < ApplicationRecord
|
|||
has_many :mentioned_accounts, through: :mentions, source: :account, class_name: 'Account'
|
||||
has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status
|
||||
has_many :media_attachments, dependent: :nullify
|
||||
has_many :reference_objects, class_name: 'StatusReference', inverse_of: :status
|
||||
has_many :references, through: :reference_objects, class_name: 'Status', source: :target_status
|
||||
has_many :referenced_by_status_objects, foreign_key: 'target_status_id', class_name: 'StatusReference', inverse_of: :target_status
|
||||
has_many :referenced_by_statuses, through: :referenced_by_status_objects, class_name: 'Status', source: :status
|
||||
|
||||
has_and_belongs_to_many :tags
|
||||
has_and_belongs_to_many :preview_cards
|
||||
|
@ -332,6 +336,10 @@ class Status < ApplicationRecord
|
|||
status_stat&.emoji_reaction_accounts_count || 0
|
||||
end
|
||||
|
||||
def status_referred_by_count
|
||||
status_stat&.status_referred_by_count || 0
|
||||
end
|
||||
|
||||
def increment_count!(key)
|
||||
update_status_stat!(key => public_send(key) + 1)
|
||||
end
|
||||
|
@ -340,6 +348,10 @@ class Status < ApplicationRecord
|
|||
update_status_stat!(key => [public_send(key) - 1, 0].max)
|
||||
end
|
||||
|
||||
def add_status_referred_by_count!(diff)
|
||||
update_status_stat!(status_referred_by_count: [public_send(:status_referred_by_count) + diff, 0].max)
|
||||
end
|
||||
|
||||
def emoji_reactions_grouped_by_name(account = nil)
|
||||
(Oj.load(status_stat&.emoji_reactions || '', mode: :strict) || []).tap do |emoji_reactions|
|
||||
if account.present?
|
||||
|
|
25
app/models/status_reference.rb
Normal file
25
app/models/status_reference.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: status_references
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# status_id :bigint(8) not null
|
||||
# target_status_id :bigint(8) not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class StatusReference < ApplicationRecord
|
||||
belongs_to :status
|
||||
belongs_to :target_status, class_name: 'Status'
|
||||
|
||||
has_one :notification, as: :activity, dependent: :destroy
|
||||
|
||||
validate :validate_status_visibilities
|
||||
|
||||
def validate_status_visibilities
|
||||
raise Mastodon::ValidationError, I18n.t('status_references.errors.invalid_status_visibilities') if [:public, :public_unlisted, :unlisted, :login].exclude?(target_status.visibility.to_sym)
|
||||
end
|
||||
end
|
|
@ -15,6 +15,7 @@
|
|||
# emoji_reactions_count :integer default(0), not null
|
||||
# test :integer default(0), not null
|
||||
# emoji_reaction_accounts_count :integer default(0), not null
|
||||
# status_referred_by_count :integer default(0), not null
|
||||
#
|
||||
|
||||
class StatusStat < ApplicationRecord
|
||||
|
@ -46,6 +47,10 @@ class StatusStat < ApplicationRecord
|
|||
[attributes['emoji_reaction_accounts_count'], 0].max
|
||||
end
|
||||
|
||||
def status_referred_by_count
|
||||
[attributes['status_referred_by_count'], 0].max
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reset_parent_cache
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
# sign_up_ip :inet
|
||||
# role_id :bigint(8)
|
||||
# settings :text
|
||||
# time_zone :string
|
||||
#
|
||||
|
||||
class User < ApplicationRecord
|
||||
|
|
|
@ -117,6 +117,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
|||
:kmyblue_markdown,
|
||||
:kmyblue_reaction_deck,
|
||||
:kmyblue_visibility_login,
|
||||
:status_reference,
|
||||
]
|
||||
|
||||
capabilities << :profile_search unless Chewy.enabled?
|
||||
|
|
|
@ -13,7 +13,7 @@ class REST::NotificationSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def status_type?
|
||||
[:favourite, :emoji_reaction, :reaction, :reblog, :status, :mention, :poll, :update].include?(object.type)
|
||||
[:favourite, :emoji_reaction, :reaction, :reblog, :status_reference, :status, :mention, :poll, :update].include?(object.type)
|
||||
end
|
||||
|
||||
def report_type?
|
||||
|
|
|
@ -6,6 +6,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
||||
:sensitive, :spoiler_text, :visibility, :visibility_ex, :language,
|
||||
:uri, :url, :replies_count, :reblogs_count, :searchability, :markdown,
|
||||
:status_reference_ids, :status_references_count, :status_referred_by_count,
|
||||
:favourites_count, :emoji_reactions, :emoji_reactions_count, :reactions, :edited_at
|
||||
|
||||
attribute :favourited, if: :current_user?
|
||||
|
@ -92,6 +93,14 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||
ActivityPub::TagManager.instance.url_for(object)
|
||||
end
|
||||
|
||||
def status_reference_ids
|
||||
@status_reference_ids = object.reference_objects.pluck(:target_status_id)
|
||||
end
|
||||
|
||||
def status_references_count
|
||||
status_reference_ids.size
|
||||
end
|
||||
|
||||
def favourited
|
||||
if instance_options && instance_options[:relationships]
|
||||
instance_options[:relationships].favourites_map[object.id] || false
|
||||
|
|
|
@ -126,6 +126,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
|
|||
:kmyblue_markdown,
|
||||
:kmyblue_reaction_deck,
|
||||
:kmyblue_visibility_login,
|
||||
:status_reference,
|
||||
]
|
||||
|
||||
capabilities << :profile_search unless Chewy.enabled?
|
||||
|
|
49
app/services/activitypub/fetch_references_service.rb
Normal file
49
app/services/activitypub/fetch_references_service.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FetchReferencesService < BaseService
|
||||
include JsonLdHelper
|
||||
|
||||
def call(status, collection_or_uri)
|
||||
@account = status.account
|
||||
|
||||
collection_items(collection_or_uri)&.map { |item| value_or_id(item) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection_items(collection_or_uri)
|
||||
collection = fetch_collection(collection_or_uri)
|
||||
return unless collection.is_a?(Hash) && collection['first'].present?
|
||||
|
||||
all_items = []
|
||||
collection = fetch_collection(collection['first'])
|
||||
|
||||
while collection.is_a?(Hash)
|
||||
items = begin
|
||||
case collection['type']
|
||||
when 'Collection', 'CollectionPage'
|
||||
collection['items']
|
||||
when 'OrderedCollection', 'OrderedCollectionPage'
|
||||
collection['orderedItems']
|
||||
end
|
||||
end
|
||||
|
||||
break if items.blank?
|
||||
|
||||
all_items.concat(items)
|
||||
|
||||
break if all_items.size >= StatusReferenceValidator::LIMIT
|
||||
|
||||
collection = collection['next'].present? ? fetch_collection(collection['next']) : nil
|
||||
end
|
||||
|
||||
all_items
|
||||
end
|
||||
|
||||
def fetch_collection(collection_or_uri)
|
||||
return collection_or_uri if collection_or_uri.is_a?(Hash)
|
||||
return if invalid_origin?(collection_or_uri)
|
||||
|
||||
fetch_resource_without_id_validation(collection_or_uri, nil, true)
|
||||
end
|
||||
end
|
|
@ -34,6 +34,7 @@ class PostStatusService < BaseService
|
|||
# @option [String] :idempotency Optional idempotency key
|
||||
# @option [Boolean] :with_rate_limit
|
||||
# @option [Enumerable] :allowed_mentions Optional array of expected mentioned account IDs, raises `UnexpectedMentionsError` if unexpected accounts end up in mentions
|
||||
# @option [Enumerable] :status_reference_ids Optional array
|
||||
# @return [Status]
|
||||
def call(account, options = {})
|
||||
@account = account
|
||||
|
@ -78,6 +79,7 @@ class PostStatusService < BaseService
|
|||
@markdown = @options[:markdown] || false
|
||||
@scheduled_at = @options[:scheduled_at]&.to_datetime
|
||||
@scheduled_at = nil if scheduled_in_the_past?
|
||||
@reference_ids = (@options[:status_reference_ids] || []).map(&:to_i).filter(&:positive?)
|
||||
rescue ArgumentError
|
||||
raise ActiveRecord::RecordInvalid
|
||||
end
|
||||
|
@ -146,6 +148,7 @@ class PostStatusService < BaseService
|
|||
|
||||
def postprocess_status!
|
||||
process_hashtags_service.call(@status)
|
||||
ProcessReferencesWorker.perform_async(@status.id, @reference_ids)
|
||||
Trends.tags.register(@status)
|
||||
LinkCrawlWorker.perform_async(@status.id)
|
||||
DistributionWorker.perform_async(@status.id)
|
||||
|
@ -221,6 +224,7 @@ class PostStatusService < BaseService
|
|||
media_attachments: @media || [],
|
||||
ordered_media_attachment_ids: (@options[:media_ids] || []).map(&:to_i) & @media.map(&:id),
|
||||
thread: @in_reply_to,
|
||||
status_reference_ids: @status_reference_ids,
|
||||
poll_attributes: poll_attributes,
|
||||
sensitive: @sensitive,
|
||||
spoiler_text: @options[:spoiler_text] || '',
|
||||
|
|
86
app/services/process_references_service.rb
Normal file
86
app/services/process_references_service.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProcessReferencesService < BaseService
|
||||
include Payloadable
|
||||
|
||||
DOMAIN = ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
|
||||
REFURL_EXP = /(RT|QT|BT|RN)((:)? +|:)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
|
||||
STATUSID_EXP = %r{(http|https)://#{DOMAIN}/@[a-zA-Z0-9]+/([0-9]{16,})}
|
||||
|
||||
def call(status, reference_parameters, save_records: true, urls: nil)
|
||||
@status = status
|
||||
@reference_parameters = reference_parameters || []
|
||||
@save_records = save_records
|
||||
@urls = urls || []
|
||||
|
||||
old_references
|
||||
|
||||
StatusReference.transaction do
|
||||
remove_old_references
|
||||
add_references
|
||||
|
||||
@status.save! if @save_records
|
||||
end
|
||||
|
||||
create_notifications!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def references
|
||||
@references = @reference_parameters + scan_text!
|
||||
end
|
||||
|
||||
def old_references
|
||||
@old_references = @status.references.pluck(:id)
|
||||
end
|
||||
|
||||
def added_references
|
||||
(references - old_references).uniq
|
||||
end
|
||||
|
||||
def removed_references
|
||||
(old_references - references).uniq
|
||||
end
|
||||
|
||||
def scan_text!
|
||||
text = @status.text.gsub(%r{</?[^>]*>}, '')
|
||||
@scan_text = fetch_statuses!(text.scan(REFURL_EXP).pluck(3).uniq).map(&:id).uniq.filter { |status_id| !status_id.zero? }
|
||||
end
|
||||
|
||||
def fetch_statuses!(urls)
|
||||
(urls + @urls)
|
||||
.map { |url| ResolveURLService.new.call(url) }
|
||||
.filter { |status| status }
|
||||
end
|
||||
|
||||
def add_references
|
||||
return if added_references.empty?
|
||||
|
||||
@added_objects = []
|
||||
|
||||
statuses = Status.where(id: added_references)
|
||||
statuses.each do |status|
|
||||
@added_objects << @status.reference_objects.new(target_status: status)
|
||||
status.increment_count!(:status_referred_by_count)
|
||||
end
|
||||
end
|
||||
|
||||
def create_notifications!
|
||||
return if @added_objects.empty?
|
||||
|
||||
LocalNotificationWorker.push_bulk(@added_objects) do |ref|
|
||||
[ref.target_status.account_id, ref.id, 'StatusReference', 'status_reference']
|
||||
end
|
||||
end
|
||||
|
||||
def remove_old_references
|
||||
return if removed_references.empty?
|
||||
|
||||
statuses = Status.where(id: removed_references)
|
||||
@status.reference_objects.where(target_status: statuses).destroy_all
|
||||
statuses.each do |status|
|
||||
status.decrement_count!(:status_referred_by_count)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -142,6 +142,7 @@ class UpdateStatusService < BaseService
|
|||
def update_metadata!
|
||||
ProcessHashtagsService.new.call(@status)
|
||||
ProcessMentionsService.new.call(@status)
|
||||
ProcessReferencesWorker.perform_async(@status.id, (@options[:status_reference_ids] || []).map(&:to_i).filter(&:positive?))
|
||||
end
|
||||
|
||||
def broadcast_updates!
|
||||
|
|
11
app/workers/process_references_worker.rb
Normal file
11
app/workers/process_references_worker.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProcessReferencesWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(status_id, ids, urls: nil)
|
||||
ProcessReferencesService.new.call(Status.find(status_id), ids || [], urls: urls)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
end
|
12
db/migrate/20230705232953_create_status_references.rb
Normal file
12
db/migrate/20230705232953_create_status_references.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateStatusReferences < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :status_references do |t|
|
||||
t.belongs_to :status, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.belongs_to :target_status, null: false, foreign_key: { on_delete: :cascade, to_table: :statuses }
|
||||
t.datetime :created_at, null: false
|
||||
t.datetime :updated_at, null: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddStatusReferredByCountToStatusStats < ActiveRecord::Migration[6.1]
|
||||
def up
|
||||
safety_assured do
|
||||
add_column :status_stats, :status_referred_by_count, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :status_stats, :status_referred_by_count
|
||||
end
|
||||
end
|
21
db/schema.rb
21
db/schema.rb
|
@ -1,5 +1,3 @@
|
|||
# rubocop:disable all
|
||||
|
||||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
|
@ -12,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
||||
ActiveRecord::Schema.define(version: 2023_07_06_031715) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -346,6 +344,7 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "dump_file_size"
|
||||
t.index ["user_id"], name: "index_backups_on_user_id"
|
||||
end
|
||||
|
||||
create_table "blocks", force: :cascade do |t|
|
||||
|
@ -804,6 +803,7 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
t.bigint "owner_id"
|
||||
t.boolean "confidential", default: true, null: false
|
||||
t.index ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type"
|
||||
t.index ["superapp"], name: "index_oauth_applications_on_superapp", where: "(superapp = true)"
|
||||
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true
|
||||
end
|
||||
|
||||
|
@ -1041,6 +1041,15 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
t.index ["status_id"], name: "index_status_pins_on_status_id"
|
||||
end
|
||||
|
||||
create_table "status_references", force: :cascade do |t|
|
||||
t.bigint "status_id", null: false
|
||||
t.bigint "target_status_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["status_id"], name: "index_status_references_on_status_id"
|
||||
t.index ["target_status_id"], name: "index_status_references_on_target_status_id"
|
||||
end
|
||||
|
||||
create_table "status_stats", force: :cascade do |t|
|
||||
t.bigint "status_id", null: false
|
||||
t.bigint "replies_count", default: 0, null: false
|
||||
|
@ -1052,6 +1061,7 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
t.integer "emoji_reactions_count", default: 0, null: false
|
||||
t.integer "test", default: 0, null: false
|
||||
t.integer "emoji_reaction_accounts_count", default: 0, null: false
|
||||
t.integer "status_referred_by_count", default: 0, null: false
|
||||
t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true
|
||||
end
|
||||
|
||||
|
@ -1211,6 +1221,7 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
t.boolean "skip_sign_in_token"
|
||||
t.bigint "role_id"
|
||||
t.text "settings"
|
||||
t.string "time_zone"
|
||||
t.index ["account_id"], name: "index_users_on_account_id"
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)"
|
||||
|
@ -1374,6 +1385,8 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
add_foreign_key "status_edits", "statuses", on_delete: :cascade
|
||||
add_foreign_key "status_pins", "accounts", name: "fk_d4cb435b62", on_delete: :cascade
|
||||
add_foreign_key "status_pins", "statuses", on_delete: :cascade
|
||||
add_foreign_key "status_references", "statuses", column: "target_status_id", on_delete: :cascade
|
||||
add_foreign_key "status_references", "statuses", on_delete: :cascade
|
||||
add_foreign_key "status_stats", "statuses", on_delete: :cascade
|
||||
add_foreign_key "status_trends", "accounts", on_delete: :cascade
|
||||
add_foreign_key "status_trends", "statuses", on_delete: :cascade
|
||||
|
@ -1491,5 +1504,3 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
|||
add_index "follow_recommendations", ["account_id"], name: "index_follow_recommendations_on_account_id", unique: true
|
||||
|
||||
end
|
||||
|
||||
# rubocop:enable all
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue