-
+
{shortNumberFormat(account.get('statuses_count'))}
-
+
{shortNumberFormat(account.get('following_count'))}
-
+
{shortNumberFormat(account.get('followers_count'))}
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 89387ca434..0ffeaa4dc1 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -355,7 +355,9 @@ export default class Status extends ImmutablePureComponent {
if (status && ancestorsIds && ancestorsIds.size > 0) {
const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
- element.scrollIntoView(true);
+ window.requestAnimationFrame(() => {
+ element.scrollIntoView(true);
+ });
this._scrolledIntoView = true;
}
}
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 47a165e169..55ea32acbe 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -158,6 +158,9 @@ export default class Video extends React.PureComponent {
this.setState({ dragging: true });
this.video.pause();
this.handleMouseMove(e);
+
+ e.preventDefault();
+ e.stopPropagation();
}
handleMouseUp = () => {
@@ -174,8 +177,10 @@ export default class Video extends React.PureComponent {
const { x } = getPointerPosition(this.seek, e);
const currentTime = Math.floor(this.video.duration * x);
- this.video.currentTime = currentTime;
- this.setState({ currentTime });
+ if (!isNaN(currentTime)) {
+ this.video.currentTime = currentTime;
+ this.setState({ currentTime });
+ }
}, 60);
togglePlay = () => {
@@ -281,6 +286,15 @@ export default class Video extends React.PureComponent {
playerStyle.height = height;
}
+ let preload;
+ if (startTime || fullscreen || dragging) {
+ preload = 'auto';
+ } else if (detailed) {
+ preload = 'metadata';
+ } else {
+ preload = 'none';
+ }
+
return (
{ with_media? || reblog? }
@@ -86,7 +87,25 @@ class Status < ApplicationRecord
scope :not_local_only, -> { where(local_only: [false, nil]) }
- cache_associated :account, :application, :media_attachments, :conversation, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, :conversation, mentions: :account], thread: :account
+ cache_associated :account,
+ :application,
+ :media_attachments,
+ :conversation,
+ :status_stat,
+ :tags,
+ :stream_entry,
+ mentions: :account,
+ reblog: [
+ :account,
+ :application,
+ :stream_entry,
+ :tags,
+ :media_attachments,
+ :conversation,
+ :status_stat,
+ mentions: :account,
+ ],
+ thread: :account
delegate :domain, to: :account, prefix: true
@@ -180,6 +199,26 @@ class Status < ApplicationRecord
@marked_for_mass_destruction
end
+ def replies_count
+ status_stat&.replies_count || 0
+ end
+
+ def reblogs_count
+ status_stat&.reblogs_count || 0
+ end
+
+ def favourites_count
+ status_stat&.favourites_count || 0
+ end
+
+ def increment_count!(key)
+ update_status_stat!(key => public_send(key) + 1)
+ end
+
+ def decrement_count!(key)
+ update_status_stat!(key => [public_send(key) - 1, 0].max)
+ end
+
after_create :increment_counter_caches
after_destroy :decrement_counter_caches
@@ -197,6 +236,10 @@ class Status < ApplicationRecord
before_validation :set_local
class << self
+ def cache_ids
+ left_outer_joins(:status_stat).select('statuses.id, greatest(statuses.updated_at, status_stats.updated_at) AS updated_at')
+ end
+
def in_chosen_languages(account)
where(language: nil).or where(language: account.chosen_languages)
end
@@ -372,6 +415,11 @@ class Status < ApplicationRecord
private
+ def update_status_stat!(attrs)
+ record = status_stat || build_status_stat
+ record.update(attrs)
+ end
+
def store_uri
update_attribute(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil?
end
@@ -434,13 +482,8 @@ class Status < ApplicationRecord
Account.where(id: account_id).update_all('statuses_count = COALESCE(statuses_count, 0) + 1')
end
- return unless reblog?
-
- if association(:reblog).loaded?
- reblog.update_attribute(:reblogs_count, reblog.reblogs_count + 1)
- else
- Status.where(id: reblog_of_id).update_all('reblogs_count = COALESCE(reblogs_count, 0) + 1')
- end
+ reblog.increment_count!(:reblogs_count) if reblog?
+ thread.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
end
def decrement_counter_caches
@@ -452,12 +495,7 @@ class Status < ApplicationRecord
Account.where(id: account_id).update_all('statuses_count = GREATEST(COALESCE(statuses_count, 0) - 1, 0)')
end
- return unless reblog?
-
- if association(:reblog).loaded?
- reblog.update_attribute(:reblogs_count, [reblog.reblogs_count - 1, 0].max)
- else
- Status.where(id: reblog_of_id).update_all('reblogs_count = GREATEST(COALESCE(reblogs_count, 0) - 1, 0)')
- end
+ reblog.decrement_count!(:reblogs_count) if reblog?
+ thread.decrement_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
end
end
diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb
new file mode 100644
index 0000000000..9d358776b4
--- /dev/null
+++ b/app/models/status_stat.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: status_stats
+#
+# id :bigint(8) not null, primary key
+# status_id :bigint(8) not null
+# replies_count :bigint(8) default(0), not null
+# reblogs_count :bigint(8) default(0), not null
+# favourites_count :bigint(8) default(0), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class StatusStat < ApplicationRecord
+ belongs_to :status, inverse_of: :status_stat
+end
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index c8fffcf68a..89776b1fcc 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -3,7 +3,8 @@
class REST::StatusSerializer < ActiveModel::Serializer
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
:sensitive, :spoiler_text, :visibility, :language,
- :uri, :content, :url, :reblogs_count, :favourites_count
+ :uri, :content, :url, :replies_count, :reblogs_count,
+ :favourites_count
attribute :favourited, if: :current_user?
attribute :reblogged, if: :current_user?
diff --git a/app/services/authorize_follow_service.rb b/app/services/authorize_follow_service.rb
index f47d488f11..1674239df7 100644
--- a/app/services/authorize_follow_service.rb
+++ b/app/services/authorize_follow_service.rb
@@ -3,7 +3,7 @@
class AuthorizeFollowService < BaseService
def call(source_account, target_account, **options)
if options[:skip_follow_request]
- follow_request = FollowRequest.new(account: source_account, target_account: target_account)
+ follow_request = FollowRequest.new(account: source_account, target_account: target_account, uri: options[:follow_request_uri])
else
follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account)
follow_request.authorize!
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index a068c1ed86..1db1917e27 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -2,11 +2,13 @@
class ResolveURLService < BaseService
include JsonLdHelper
+ include Authorization
attr_reader :url
- def call(url)
+ def call(url, on_behalf_of: nil)
@url = url
+ @on_behalf_of = on_behalf_of
return process_local_url if local_url?
@@ -84,6 +86,10 @@ class ResolveURLService < BaseService
def check_local_status(status)
return if status.nil?
- status if status.public_visibility? || status.unlisted_visibility?
+ authorize_with @on_behalf_of, status, :show?
+ status
+ rescue Mastodon::NotPermittedError
+ # Do not disclose the existence of status the user is not authorized to see
+ nil
end
end
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 5bb395942d..cc1fcb52f0 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -53,7 +53,7 @@ class SearchService < BaseService
end
def url_resource
- @_url_resource ||= ResolveURLService.new.call(query)
+ @_url_resource ||= ResolveURLService.new.call(query, on_behalf_of: @account)
end
def url_resource_symbol
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index d3b9893c41..caf03bd7cb 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -14,17 +14,17 @@
.public-account-header__tabs__tabs
.details-counters
.counter{ class: active_nav_class(short_account_url(account)) }
- = link_to short_account_url(account), class: 'u-url u-uid' do
+ = link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do
%span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
%span.counter-label= t('accounts.posts')
.counter{ class: active_nav_class(account_following_index_url(account)) }
- = link_to account_following_index_url(account) do
+ = link_to account_following_index_url(account), title: number_with_delimiter(account.following_count) do
%span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true
%span.counter-label= t('accounts.following')
.counter{ class: active_nav_class(account_followers_url(account)) }
- = link_to account_followers_url(account) do
+ = link_to account_followers_url(account), title: number_with_delimiter(account.followers_count) do
%span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true
%span.counter-label= t('accounts.followers')
.spacer
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 5e174f312e..5b410ec84d 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -3,11 +3,13 @@
= f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
.batch-table__row__content
.status__content><
- - unless status.proper.spoiler_text.blank?
- %p><
- %strong> Content warning: #{Formatter.instance.format_spoiler(status.proper)}
-
- = Formatter.instance.format(status.proper, custom_emojify: true)
+ - if status.proper.spoiler_text.blank?
+ = Formatter.instance.format(status.proper, custom_emojify: true)
+ - else
+ %details<
+ %summary><
+ %strong> Content warning: #{Formatter.instance.format_spoiler(status.proper)}
+ = Formatter.instance.format(status.proper, custom_emojify: true)
- unless status.proper.media_attachments.empty?
- if status.proper.media_attachments.first.video?
diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
index 30cd269140..ef2d2b8941 100644
--- a/app/views/settings/exports/show.html.haml
+++ b/app/views/settings/exports/show.html.haml
@@ -8,17 +8,25 @@
%th= t('exports.storage')
%td= number_to_human_size @export.total_storage
%td
+ %tr
+ %th= t('accounts.statuses')
+ %td= number_with_delimiter @export.total_statuses
+ %td
%tr
%th= t('exports.follows')
- %td= number_to_human @export.total_follows
+ %td= number_with_delimiter @export.total_follows
%td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
+ %tr
+ %th= t('accounts.followers')
+ %td= number_with_delimiter @export.total_followers
+ %td
%tr
%th= t('exports.blocks')
- %td= number_to_human @export.total_blocks
+ %td= number_with_delimiter @export.total_blocks
%td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv)
%tr
%th= t('exports.mutes')
- %td= number_to_human @export.total_mutes
+ %td= number_with_delimiter @export.total_mutes
%td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv)
%p.muted-hint= t('exports.archive_takeout.hint_html')
diff --git a/app/views/settings/imports/show.html.haml b/app/views/settings/imports/show.html.haml
index 991dd4e941..2b43cb1349 100644
--- a/app/views/settings/imports/show.html.haml
+++ b/app/views/settings/imports/show.html.haml
@@ -1,11 +1,14 @@
- content_for :page_title do
= t('settings.import')
-%p.hint= t('imports.preface')
-
= simple_form_for @import, url: settings_import_path do |f|
- = f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
- = f.input :data, wrapper: :with_label, hint: t('simple_form.hints.imports.data')
+ %p.hint= t('imports.preface')
+
+ .field-group
+ = f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+
+ .field-group
+ = f.input :data, wrapper: :with_block_label, hint: t('simple_form.hints.imports.data')
.actions
= f.button :button, t('imports.upload'), type: :submit
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 8532c9d9a2..cd9bacf680 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -59,6 +59,8 @@ module Devise
@@ldap_password = nil
mattr_accessor :ldap_tls_no_verify
@@ldap_tls_no_verify = false
+ mattr_accessor :ldap_search_filter
+ @@ldap_search_filter = nil
class Strategies::PamAuthenticatable
def valid?
@@ -362,5 +364,6 @@ Devise.setup do |config|
config.ldap_password = ENV.fetch('LDAP_PASSWORD')
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
+ config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '%{uid}=%{email}')
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 4ed868c8bc..ecabd9a36f 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -184,6 +184,7 @@ en:
unsuspend_account: "%{name} unsuspended %{target}'s account"
update_custom_emoji: "%{name} updated emoji %{target}"
update_status: "%{name} updated status by %{target}"
+ deleted_status: "(deleted status)"
title: Audit log
custom_emojis:
by_domain: Domain
@@ -401,6 +402,7 @@ en:
media:
title: Media
no_media: No media
+ no_status_selected: No statuses were changed as none were selected
title: Account statuses
with_media: With media
subscriptions:
diff --git a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
index 120f744026..3369e3b9e9 100644
--- a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
+++ b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
@@ -1,5 +1,3 @@
-require Rails.root.join('lib', 'mastodon', 'migration_helpers')
-
class ChangeAccountIdNonnullableInLists < ActiveRecord::Migration[5.1]
def change
change_column_null :lists, :account_id, false
diff --git a/db/migrate/20180528141303_fix_accounts_unique_index.rb b/db/migrate/20180528141303_fix_accounts_unique_index.rb
index 624ea229e4..bd4e158b7e 100644
--- a/db/migrate/20180528141303_fix_accounts_unique_index.rb
+++ b/db/migrate/20180528141303_fix_accounts_unique_index.rb
@@ -6,6 +6,10 @@ class FixAccountsUniqueIndex < ActiveRecord::Migration[5.2]
def local?
domain.nil?
end
+
+ def acct
+ local? ? username : "#{username}@#{domain}"
+ end
end
disable_ddl_transaction!
diff --git a/db/migrate/20180812162710_create_status_stats.rb b/db/migrate/20180812162710_create_status_stats.rb
new file mode 100644
index 0000000000..d4da36fe78
--- /dev/null
+++ b/db/migrate/20180812162710_create_status_stats.rb
@@ -0,0 +1,12 @@
+class CreateStatusStats < ActiveRecord::Migration[5.2]
+ def change
+ create_table :status_stats do |t|
+ t.belongs_to :status, null: false, foreign_key: { on_delete: :cascade }, index: { unique: true }
+ t.bigint :replies_count, null: false, default: 0
+ t.bigint :reblogs_count, null: false, default: 0
+ t.bigint :favourites_count, null: false, default: 0
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20180812173710_copy_status_stats.rb b/db/migrate/20180812173710_copy_status_stats.rb
new file mode 100644
index 0000000000..64a564ca07
--- /dev/null
+++ b/db/migrate/20180812173710_copy_status_stats.rb
@@ -0,0 +1,19 @@
+class CopyStatusStats < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def up
+ safety_assured do
+ execute <<-SQL.squish
+ INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at)
+ SELECT id, reblogs_count, favourites_count, created_at, updated_at
+ FROM statuses
+ ON CONFLICT (status_id) DO UPDATE
+ SET reblogs_count = EXCLUDED.reblogs_count, favourites_count = EXCLUDED.favourites_count
+ SQL
+ end
+ end
+
+ def down
+ # Nothing
+ end
+end
diff --git a/db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb b/db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb
new file mode 100644
index 0000000000..7077a4e659
--- /dev/null
+++ b/db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb
@@ -0,0 +1,23 @@
+require Rails.root.join('lib', 'mastodon', 'migration_helpers')
+
+class AddConfidentialToDoorkeeperApplication < ActiveRecord::Migration[5.2]
+ include Mastodon::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ safety_assured do
+ add_column_with_default(
+ :oauth_applications,
+ :confidential,
+ :boolean,
+ allow_null: false,
+ default: true # maintaining backwards compatibility: require secrets
+ )
+ end
+ end
+
+ def down
+ remove_column :oauth_applications, :confidential
+ end
+end
diff --git a/db/post_migrate/20180813113448_copy_status_stats_cleanup.rb b/db/post_migrate/20180813113448_copy_status_stats_cleanup.rb
new file mode 100644
index 0000000000..f3ae772c79
--- /dev/null
+++ b/db/post_migrate/20180813113448_copy_status_stats_cleanup.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class CopyStatusStatsCleanup < ActiveRecord::Migration[5.2]
+ disable_ddl_transaction!
+
+ def change
+ safety_assured do
+ remove_column :statuses, :reblogs_count, :integer, default: 0, null: false
+ remove_column :statuses, :favourites_count, :integer, default: 0, null: false
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c2a313ffa3..bac85869a5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2018_08_13_160548) do
+ActiveRecord::Schema.define(version: 2018_08_14_171349) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -359,6 +359,7 @@ ActiveRecord::Schema.define(version: 2018_08_13_160548) do
t.string "website"
t.string "owner_type"
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 ["uid"], name: "index_oauth_applications_on_uid", unique: true
end
@@ -466,6 +467,16 @@ ActiveRecord::Schema.define(version: 2018_08_13_160548) do
t.index ["account_id", "status_id"], name: "index_status_pins_on_account_id_and_status_id", unique: true
end
+ create_table "status_stats", force: :cascade do |t|
+ t.bigint "status_id", null: false
+ t.bigint "replies_count", default: 0, null: false
+ t.bigint "reblogs_count", default: 0, null: false
+ t.bigint "favourites_count", default: 0, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true
+ end
+
create_table "statuses", id: :bigint, default: -> { "timestamp_id('statuses'::text)" }, force: :cascade do |t|
t.string "uri"
t.text "text", default: "", null: false
@@ -478,8 +489,6 @@ ActiveRecord::Schema.define(version: 2018_08_13_160548) do
t.integer "visibility", default: 0, null: false
t.text "spoiler_text", default: "", null: false
t.boolean "reply", default: false, null: false
- t.integer "favourites_count", default: 0, null: false
- t.integer "reblogs_count", default: 0, null: false
t.string "language"
t.bigint "conversation_id"
t.boolean "local"
@@ -643,6 +652,7 @@ ActiveRecord::Schema.define(version: 2018_08_13_160548) do
add_foreign_key "session_activations", "users", name: "fk_e5fda67334", 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_stats", "statuses", on_delete: :cascade
add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", name: "fk_c7fa917661", on_delete: :nullify
add_foreign_key "statuses", "accounts", name: "fk_9bda1543f7", on_delete: :cascade
add_foreign_key "statuses", "statuses", column: "in_reply_to_id", on_delete: :nullify
diff --git a/lib/devise/ldap_authenticatable.rb b/lib/devise/ldap_authenticatable.rb
index ef786fbb77..534c7a8517 100644
--- a/lib/devise/ldap_authenticatable.rb
+++ b/lib/devise/ldap_authenticatable.rb
@@ -24,7 +24,8 @@ module Devise
connect_timeout: 10
)
- if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password))
+ filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, email: email)
+ if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: filter, password: password))
user = User.ldap_get_user(user_info.first)
success!(user)
else
diff --git a/spec/fabricators/status_stat_fabricator.rb b/spec/fabricators/status_stat_fabricator.rb
new file mode 100644
index 0000000000..9c67fd404a
--- /dev/null
+++ b/spec/fabricators/status_stat_fabricator.rb
@@ -0,0 +1,6 @@
+Fabricator(:status_stat) do
+ status_id nil
+ replies_count ""
+ reblogs_count ""
+ favourites_count ""
+end
diff --git a/spec/lib/activitypub/activity/undo_spec.rb b/spec/lib/activitypub/activity/undo_spec.rb
index e01c5e03e7..9545e1f46a 100644
--- a/spec/lib/activitypub/activity/undo_spec.rb
+++ b/spec/lib/activitypub/activity/undo_spec.rb
@@ -52,6 +52,32 @@ RSpec.describe ActivityPub::Activity::Undo do
end
end
+ context 'with Accept' do
+ let(:recipient) { Fabricate(:account) }
+ let(:object_json) do
+ {
+ id: 'bar',
+ type: 'Accept',
+ actor: ActivityPub::TagManager.instance.uri_for(sender),
+ object: 'follow-to-revoke',
+ }
+ end
+
+ before do
+ recipient.follow!(sender, uri: 'follow-to-revoke')
+ end
+
+ it 'deletes follow from recipient to sender' do
+ subject.perform
+ expect(recipient.following?(sender)).to be false
+ end
+
+ it 'creates a follow request from recipient to sender' do
+ subject.perform
+ expect(recipient.requested?(sender)).to be true
+ end
+ end
+
context 'with Block' do
let(:recipient) { Fabricate(:account) }
diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb
index 6daa461457..277dcc5264 100644
--- a/spec/models/export_spec.rb
+++ b/spec/models/export_spec.rb
@@ -48,17 +48,17 @@ describe Export do
describe 'total_follows' do
it 'returns the total number of the followed accounts' do
target_accounts.each(&account.method(:follow!))
- expect(Export.new(account).total_follows).to eq 2
+ expect(Export.new(account.reload).total_follows).to eq 2
end
it 'returns the total number of the blocked accounts' do
target_accounts.each(&account.method(:block!))
- expect(Export.new(account).total_blocks).to eq 2
+ expect(Export.new(account.reload).total_blocks).to eq 2
end
it 'returns the total number of the muted accounts' do
target_accounts.each(&account.method(:mute!))
- expect(Export.new(account).total_mutes).to eq 2
+ expect(Export.new(account.reload).total_mutes).to eq 2
end
end
end
diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index 43175d8526..f221973b6d 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -37,4 +37,20 @@ RSpec.describe Follow, type: :model do
expect(a[1]).to eq follow0
end
end
+
+ describe 'revoke_request!' do
+ let(:follow) { Fabricate(:follow, account: account, target_account: target_account) }
+ let(:account) { Fabricate(:account) }
+ let(:target_account) { Fabricate(:account) }
+
+ it 'revokes the follow relation' do
+ follow.revoke_request!
+ expect(account.following?(target_account)).to be false
+ end
+
+ it 'creates a follow request' do
+ follow.revoke_request!
+ expect(account.requested?(target_account)).to be true
+ end
+ end
end
diff --git a/spec/models/status_stat_spec.rb b/spec/models/status_stat_spec.rb
new file mode 100644
index 0000000000..5e9351aff4
--- /dev/null
+++ b/spec/models/status_stat_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe StatusStat, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 673de52338..671080f1d9 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -29,7 +29,7 @@ describe SearchService, type: :service do
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, 10)
- expect(service).to have_received(:call).with(@query)
+ expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results
end
end
@@ -41,7 +41,7 @@ describe SearchService, type: :service do
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, 10)
- expect(service).to have_received(:call).with(@query)
+ expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(accounts: [account])
end
end
@@ -53,7 +53,7 @@ describe SearchService, type: :service do
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, 10)
- expect(service).to have_received(:call).with(@query)
+ expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(statuses: [status])
end
end