From 2db51e2f4cc55c520634df8ca34b58742ded0153 Mon Sep 17 00:00:00 2001 From: multiple creatures Date: Sun, 14 Apr 2019 15:22:55 -0500 Subject: [PATCH] Refactored community-curated world timeline code; **privacy**: remove support for packmate-visible hashtags until we resolve federation caveats. --- .../api/v1/statuses/bookmarks_controller.rb | 9 +++ app/models/status.rb | 60 ++++++------------- app/services/fan_out_on_write_service.rb | 29 ++++----- app/services/favourite_service.rb | 11 +++- app/services/reblog_service.rb | 8 +++ .../20190414162149_add_curated_to_status.rb | 5 ++ db/schema.rb | 3 + 7 files changed, 62 insertions(+), 63 deletions(-) create mode 100644 db/migrate/20190414162149_add_curated_to_status.rb diff --git a/app/controllers/api/v1/statuses/bookmarks_controller.rb b/app/controllers/api/v1/statuses/bookmarks_controller.rb index bb9729cf5..267f9dff7 100644 --- a/app/controllers/api/v1/statuses/bookmarks_controller.rb +++ b/app/controllers/api/v1/statuses/bookmarks_controller.rb @@ -30,10 +30,19 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController bookmark = Bookmark.find_or_create_by!(account: current_user.account, status: requested_status) + curate_status(requested_status) + bookmark.status.reload end def requested_status Status.find(params[:status_id]) end + + def curate_status(status) + return if status.curated + status.curated = true + status.save + FanOutOnWriteService.new.call(status) + end end diff --git a/app/models/status.rb b/app/models/status.rb index 506dca39a..fa16ede49 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -24,6 +24,8 @@ # local_only :boolean # poll_id :bigint(8) # content_type :string +# tsv :tsvector +# curated :boolean # class Status < ApplicationRecord @@ -89,13 +91,13 @@ class Status < ApplicationRecord scope :remote, -> { where(local: false).or(where.not(uri: nil)) } scope :local, -> { where(local: true).or(where(uri: nil)) } scope :network, -> { where(local: true).or(where(uri: nil)).or(where('statuses.uri LIKE ANY (array[?])', LOCAL_URIS)) } + scope :curated, -> { where(curated: true) } scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') } scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') } scope :reblogs, -> { where('statuses.reblog_of_id IS NOT NULL') } # all reblogs scope :with_public_visibility, -> { where(visibility: :public) } scope :public_browsable, -> { where(visibility: [:public, :unlisted]) } - scope :visible_to, ->(account) { where(visibility: [:public, :unlisted]).or(where(account: [account] + account.following).where(visibility: :private)) } scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) } scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced_at: nil }) } scope :including_silenced_accounts, -> { left_outer_joins(:account).where.not(accounts: { silenced_at: nil }) } @@ -306,7 +308,7 @@ class Status < ApplicationRecord if account.present? query = query .or(scope.where(account: account)) - .or(scope.where(account: account.following, visibility: [:unlisted, :private])) + .or(scope.where(account: account.following, visibility: [:private, :unlisted])) .or(scope.where(id: account.mentions.select(:status_id))) end query = query.where(reblog_of_id: nil).limit(limit) @@ -367,47 +369,26 @@ class Status < ApplicationRecord end def as_public_timeline(account = nil, local_only = false) - if account.present? && account&.user&.setting_rawr_federated - query = timeline_scope(local_only).without_replies - else - # instead of our ftl being a noisy irrelevant firehose - # only show public stuff boosted/faved by the community + if local_only query = Status.network - if local_only then - # we don't want to change the ltl - query = query - .with_public_visibility - .without_replies - .without_reblogs - else # but on the ftl - query = query.without_replies unless Setting.show_replies_in_public_timelines - # grab the stuff we faved - fav_query = Favourite.select('status_id') - .where(account_id: Account.local) - .reorder(:status_id) - .distinct - - # We need to find a new way to do this because it's much too slow. - - # grab the stuff we boosted - #boost_query = query.reblogs.select(:reblog_of_id) - # .reorder(nil) - # .distinct - # map those ids to actual statuses - - query = Status.where(id: fav_query) - .without_replies - .with_public_visibility - end + .with_public_visibility + .without_replies + .without_reblogs + elsif account.nil? || account&.user&.setting_rawr_federated + query = timeline_scope(local_only) + query = query.without_replies unless Setting.show_replies_in_public_timelines + else + scope = Status.curated + scope = scope.without_replies unless Setting.show_replies_in_public_timelines + query = scope.public_browsable + .or(scope.where(account: account.following, visibility: :private)) end apply_timeline_filters(query, account, local_only) end def as_tag_timeline(tag, account = nil, local_only = false) - query = (account.nil?) ? browsable_timeline_scope(local_only) : user_timeline_scope(account, local_only) - query = query.tagged_with(tag) - + query = browsable_timeline_scope(local_only).tagged_with(tag) apply_timeline_filters(query, account, local_only) end @@ -496,13 +477,6 @@ class Status < ApplicationRecord .without_reblogs end - def user_timeline_scope(account, local_only = false) - starting_scope = local_only ? Status.local : Status - starting_scope - .visible_to(account) - .without_reblogs - end - def apply_timeline_filters(query, account, local_only) if account.nil? filter_timeline_default(query) diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index a81482dd6..0dd1ec7ec 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -3,7 +3,7 @@ class FanOutOnWriteService < BaseService # Push a status into home and mentions feeds # @param [Status] status - def call(status, allow_nonlocal = false, deliver_to_local = true) + def call(status) raise Mastodon::RaceConditionError if status.visibility.nil? deliver_to_self(status) if status.account.local? @@ -24,25 +24,21 @@ class FanOutOnWriteService < BaseService return if status.reblog? && !Setting.show_reblogs_in_public_timelines return if status.account.silenced? - deliver_to_hashtags(status) if !status.reblog? && status.distributable? - - # we want to let community users decide what goes on the ftl with boosts - return unless allow_nonlocal || status.network? || status.relayed? - - if status.reblog? then - status = Status.find(status.reblog_of_id) - render_anonymous_payload(status) - deliver_to_local = status.network? + if !status.reblog? && status.distributable? + deliver_to_hashtags(status) + deliver_to_public(status) if status.curated end - return if status.account.silenced? || !status.public_visibility? + if status.relayed? + status = Status.find(status.reblog_of_id) + return if status.account.silenced? + render_anonymous_payload(status) + end + + return unless status.network? && status.public_visibility? && !status.reblog return if status.reply? && status.in_reply_to_account_id != status.account_id && !Setting.show_replies_in_public_timelines - if deliver_to_local then - deliver_to_local(status) - else - deliver_to_public(status) - end + deliver_to_local(status) end private @@ -104,7 +100,6 @@ class FanOutOnWriteService < BaseService def deliver_to_local(status) Rails.logger.debug "Delivering status #{status.id} to local timeline" - return unless status.network? Redis.current.publish('timeline:public:local', @payload) Redis.current.publish('timeline:public:local:media', @payload) if status.media_attachments.any? end diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index fd7ade02b..42e137564 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -16,9 +16,7 @@ class FavouriteService < BaseService favourite = Favourite.create!(account: account, status: status) - # stream it to the world timeline if public - FanOutOnWriteService.new.call(status, true, false) if status.public_visibility? - + curate_status(status) create_notification(favourite) bump_potential_friendship(account, status) @@ -56,4 +54,11 @@ class FavouriteService < BaseService def build_xml(favourite) OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.favourite_salmon(favourite)) end + + def curate_status(status) + return if status.curated + status.curated = true + status.save + FanOutOnWriteService.new.call(status) + end end diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb index 77ec52ab8..049e915df 100644 --- a/app/services/reblog_service.rb +++ b/app/services/reblog_service.rb @@ -29,6 +29,7 @@ class ReblogService < BaseService ActivityPub::DistributionWorker.perform_async(reblog.id) end + curate_status(reblogged_status) create_notification(reblog) bump_potential_friendship(account, reblog) @@ -62,4 +63,11 @@ class ReblogService < BaseService adapter: ActivityPub::Adapter ).as_json).sign!(reblog.account)) end + + def curate_status(status) + return if status.curated + status.curated = true + status.save + FanOutOnWriteService.new.call(status) + end end diff --git a/db/migrate/20190414162149_add_curated_to_status.rb b/db/migrate/20190414162149_add_curated_to_status.rb new file mode 100644 index 000000000..2ae1a0ac3 --- /dev/null +++ b/db/migrate/20190414162149_add_curated_to_status.rb @@ -0,0 +1,5 @@ +class AddCuratedToStatus < ActiveRecord::Migration[5.2] + def change + add_column :statuses, :curated, :boolean, null: true, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index be9818fa2..993c94d2e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -639,10 +639,13 @@ ActiveRecord::Schema.define(version: 2019_05_19_130537) do t.boolean "local_only" t.bigint "poll_id" t.string "content_type" + t.tsvector "tsv" + t.boolean "curated" t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20180106", order: { id: :desc } t.index ["in_reply_to_account_id"], name: "index_statuses_on_in_reply_to_account_id" t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id" t.index ["reblog_of_id", "account_id"], name: "index_statuses_on_reblog_of_id_and_account_id" + t.index ["tsv"], name: "tsv_idx", using: :gin t.index ["uri"], name: "index_statuses_on_uri", unique: true end