@@ -596,6 +600,9 @@ class Status extends ImmutablePureComponent {
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
+ {withReference}
+ {withExpiration}
+ {withLimited}
{status.get('edited_at') && *}
diff --git a/app/javascript/mastodon/features/reaction_deck/index.jsx b/app/javascript/mastodon/features/reaction_deck/index.jsx
index 7492d3cfb7..ebc2985abc 100644
--- a/app/javascript/mastodon/features/reaction_deck/index.jsx
+++ b/app/javascript/mastodon/features/reaction_deck/index.jsx
@@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
+import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
import { updateReactionDeck } from 'mastodon/actions/reaction_deck';
import Button from 'mastodon/components/button';
diff --git a/app/lib/status_cache_hydrator.rb b/app/lib/status_cache_hydrator.rb
index f6448b15ac..e4a00fa768 100644
--- a/app/lib/status_cache_hydrator.rb
+++ b/app/lib/status_cache_hydrator.rb
@@ -35,7 +35,7 @@ class StatusCacheHydrator
payload[:reblog][:bookmarked] = Bookmark.where(account_id: account_id, status_id: @status.reblog_of_id).exists?
payload[:reblog][:pinned] = StatusPin.where(account_id: account_id, status_id: @status.reblog_of_id).exists? if @status.reblog.account_id == account_id
payload[:reblog][:filtered] = payload[:filtered]
- payload[:reblog][:emoji_reactions] = @status.emoji_reactions_grouped_by_name(account)
+ payload[:reblog][:emoji_reactions] = @status.reblog.emoji_reactions_grouped_by_name(account)
if payload[:reblog][:poll]
if @status.reblog.account_id == account_id
diff --git a/app/models/antenna.rb b/app/models/antenna.rb
index c14892e138..a35fa6dd17 100644
--- a/app/models/antenna.rb
+++ b/app/models/antenna.rb
@@ -92,142 +92,6 @@ class Antenna < ApplicationRecord
context
end
- def list=(list_id)
- list_id = list_id.to_i if list_id.is_a?(String)
- if list_id.is_a?(Numeric)
- self[:list_id] = list_id
- else
- self[:list] = list_id
- end
- end
-
- def keywords_raw
- return '' if keywords.blank?
-
- keywords.join("\n")
- end
-
- def keywords_raw=(raw)
- keywords = raw.split(/\R/).filter { |r| r.present? && r.length >= 2 }.uniq
- self[:keywords] = keywords
- self[:any_keywords] = keywords.none?
- end
-
- def exclude_keywords_raw
- return '' if exclude_keywords.blank?
-
- exclude_keywords.join("\n")
- end
-
- def exclude_keywords_raw=(raw)
- exclude_keywords = raw.split(/\R/).filter(&:present?).uniq
- self[:exclude_keywords] = exclude_keywords
- end
-
- def tags_raw
- antenna_tags.where(exclude: false).map { |tag| tag.tag.name }.join("\n")
- end
-
- def tags_raw=(raw)
- return if tags_raw == raw
-
- tag_names = raw.split(/\R/).filter(&:present?).map { |r| r.start_with?('#') ? r[1..] : r }.uniq
-
- antenna_tags.where(exclude: false).destroy_all
- Tag.find_or_create_by_names(tag_names).each do |tag|
- antenna_tags.create!(tag: tag, exclude: false)
- end
- self[:any_tags] = tag_names.none?
- end
-
- def exclude_tags_raw
- return '' if exclude_tags.blank?
-
- Tag.where(id: exclude_tags).map(&:name).join("\n")
- end
-
- def exclude_tags_raw=(raw)
- return if exclude_tags_raw == raw
-
- tags = []
- tag_names = raw.split(/\R/).filter(&:present?).map { |r| r.start_with?('#') ? r[1..] : r }.uniq
- Tag.find_or_create_by_names(tag_names).each do |tag|
- tags << tag.id
- end
- self[:exclude_tags] = tags
- end
-
- def domains_raw
- antenna_domains.where(exclude: false).map(&:name).join("\n")
- end
-
- def domains_raw=(raw)
- return if domains_raw == raw
-
- domain_names = raw.split(/\R/).filter(&:present?).uniq
-
- antenna_domains.where(exclude: false).destroy_all
- domain_names.each do |domain|
- antenna_domains.create!(name: domain, exclude: false)
- end
- self[:any_domains] = domain_names.none?
- end
-
- def exclude_domains_raw
- return '' if exclude_domains.blank?
-
- exclude_domains.join("\n")
- end
-
- def exclude_domains_raw=(raw)
- return if exclude_domains_raw == raw
-
- domain_names = raw.split(/\R/).filter(&:present?).uniq
- self[:exclude_domains] = domain_names
- end
-
- def accounts_raw
- antenna_accounts.where(exclude: false).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n")
- end
-
- def accounts_raw=(raw)
- return if accounts_raw == raw
-
- account_names = raw.split(/\R/).filter(&:present?).map { |r| r.start_with?('@') ? r[1..] : r }.uniq
-
- hit = false
- antenna_accounts.where(exclude: false).destroy_all
- account_names.each do |name|
- username, domain = name.split('@')
- account = Account.find_by(username: username, domain: domain)
- if account.present?
- antenna_accounts.create!(account: account, exclude: false)
- hit = true
- end
- end
- self[:any_accounts] = !hit
- end
-
- def exclude_accounts_raw
- return '' if exclude_accounts.blank?
-
- Account.where(id: exclude_accounts).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n")
- end
-
- def exclude_accounts_raw=(raw)
- return if exclude_accounts_raw == raw
-
- account_names = raw.split(/\R/).filter(&:present?).map { |r| r.start_with?('@') ? r[1..] : r }.uniq
-
- accounts = []
- account_names.each do |name|
- username, domain = name.split('@')
- account = Account.find_by(username: username, domain: domain)
- accounts << account.id if account.present?
- end
- self[:exclude_accounts] = accounts
- end
-
private
def validate_limit
diff --git a/app/models/public_feed.rb b/app/models/public_feed.rb
index 1ed68e45e9..a641e77039 100644
--- a/app/models/public_feed.rb
+++ b/app/models/public_feed.rb
@@ -28,7 +28,8 @@ class PublicFeed
scope.merge!(account_filters_scope) if account?
scope.merge!(media_only_scope) if media_only?
scope.merge!(language_scope) if account&.chosen_languages.present?
- scope.merge!(anonymous_scope) unless account?
+ # scope.merge!(anonymous_scope) unless account?
+ scope = to_anonymous_scope(scope) unless account?
scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
end
@@ -105,6 +106,10 @@ class PublicFeed
local_only? ? Status.where(visibility: [:public, :public_unlisted]) : Status.where(visibility: :public)
end
+ def to_anonymous_scope(scope)
+ scope.where.not(visibility: :login)
+ end
+
def account_filters_scope
Status.not_excluded_by_account(account).tap do |scope|
scope.merge!(Status.not_domain_blocked_by_account(account)) unless local_only?
diff --git a/app/models/status.rb b/app/models/status.rb
index c6c05e95e7..d4a28220d1 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -173,6 +173,8 @@ class Status < ApplicationRecord
:tags,
:preview_cards,
:preloadable_poll,
+ :reference_objects,
+ :scheduled_expiration_status,
account: [:account_stat, user: :role],
active_mentions: { account: :account_stat },
reblog: [
@@ -183,6 +185,8 @@ class Status < ApplicationRecord
:conversation,
:status_stat,
:preloadable_poll,
+ :reference_objects,
+ :scheduled_expiration_status,
account: [:account_stat, user: :role],
active_mentions: { account: :account_stat },
],
@@ -353,21 +357,30 @@ class Status < ApplicationRecord
return [] if account.present? && !self.account.show_emoji_reaction?(account)
return [] if account.nil? && !options[:force] && self.account.emoji_reaction_policy != :allow
+ permitted_account_ids = options[:permitted_account_ids]
+
(Oj.load(status_stat&.emoji_reactions || '', mode: :strict) || []).tap do |emoji_reactions|
if account.present?
- remove_emoji_reactions = []
+ public_emoji_reactions = []
+
emoji_reactions.each do |emoji_reaction|
emoji_reaction['me'] = emoji_reaction['account_ids'].include?(account.id.to_s)
emoji_reaction['account_ids'] -= account.excluded_from_timeline_account_ids.map(&:to_s)
- accounts = Account.where(id: emoji_reaction['account_ids'], silenced_at: nil, suspended_at: nil)
- accounts = accounts.where('domain IS NULL OR domain NOT IN (?)', account.excluded_from_timeline_domains) if account.excluded_from_timeline_domains.size.positive?
- emoji_reaction['account_ids'] = accounts.pluck(:id).map(&:to_s)
+ accounts = []
+ if permitted_account_ids
+ emoji_reaction['account_ids'] = emoji_reaction['account_ids'] & permitted_account_ids.map(&:to_s)
+ else
+ accounts = Account.where(id: emoji_reaction['account_ids'], silenced_at: nil, suspended_at: nil)
+ accounts = accounts.where('domain IS NULL OR domain NOT IN (?)', account.excluded_from_timeline_domains) if account.excluded_from_timeline_domains.size.positive?
+ emoji_reaction['account_ids'] = accounts.pluck(:id).map(&:to_s)
+ end
emoji_reaction['count'] = emoji_reaction['account_ids'].size
- remove_emoji_reactions << emoji_reaction if emoji_reaction['count'] <= 0
+ public_emoji_reactions << emoji_reaction if (emoji_reaction['count']).positive?
end
- emoji_reactions - remove_emoji_reactions
+
+ public_emoji_reactions
end
end
end
@@ -459,6 +472,11 @@ class Status < ApplicationRecord
EmojiReaction.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |e, h| h[e.status_id] = true }
end
+ def emoji_reaction_allows_map(status_ids, account_id)
+ my_account = Account.find_by(id: account_id)
+ Status.where(id: status_ids).pluck(:account_id).uniq.index_with { |a| Account.find_by(id: a).show_emoji_reaction?(my_account) }
+ end
+
def reload_stale_associations!(cached_items)
account_ids = []
diff --git a/app/models/status_reference.rb b/app/models/status_reference.rb
index 2ed5b2aee9..62be6df3b1 100644
--- a/app/models/status_reference.rb
+++ b/app/models/status_reference.rb
@@ -18,8 +18,16 @@ class StatusReference < ApplicationRecord
has_one :notification, as: :activity, dependent: :destroy
validate :validate_status_visibilities
+ after_commit :reset_parent_cache
+
+ private
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
+
+ def reset_parent_cache
+ Rails.cache.delete("statuses/#{status_id}")
+ Rails.cache.delete("statuses/#{target_status_id}")
+ end
end
diff --git a/app/models/tag_feed.rb b/app/models/tag_feed.rb
index 14f4afe737..9c679a0249 100644
--- a/app/models/tag_feed.rb
+++ b/app/models/tag_feed.rb
@@ -32,7 +32,8 @@ class TagFeed < PublicFeed
scope.merge!(remote_only_scope) if remote_only? || hide_local_users?
scope.merge!(account_filters_scope) if account?
scope.merge!(media_only_scope) if media_only?
- scope.merge!(anonymous_scope) unless account?
+ # scope.merge!(anonymous_scope) unless account?
+ scope = to_anonymous_scope(scope) unless account?
scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
end
diff --git a/app/presenters/emoji_reaction_accounts_presenter.rb b/app/presenters/emoji_reaction_accounts_presenter.rb
new file mode 100644
index 0000000000..1b683f3366
--- /dev/null
+++ b/app/presenters/emoji_reaction_accounts_presenter.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class EmojiReactionAccountsPresenter
+ attr_reader :permitted_account_ids
+
+ def initialize(statuses, current_account_id = nil, **_options)
+ @current_account_id = current_account_id
+
+ statuses = statuses.compact
+ status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq.compact
+ emoji_reactions = EmojiReaction.where(status_id: status_ids)
+ account_ids = emoji_reactions.pluck(:account_id).uniq
+
+ permitted_accounts = Account.where(id: account_ids, silenced_at: nil, suspended_at: nil)
+ if current_account_id.present?
+ account = Account.find(current_account_id)
+ permitted_accounts = permitted_accounts.where('domain IS NULL OR domain NOT IN (?)', account.excluded_from_timeline_domains) if account.present? && account.excluded_from_timeline_domains.size.positive?
+ end
+
+ @permitted_account_ids = permitted_accounts.pluck(:id)
+ end
+end
diff --git a/app/presenters/status_relationships_presenter.rb b/app/presenters/status_relationships_presenter.rb
index 3a5f0e1589..9e55742403 100644
--- a/app/presenters/status_relationships_presenter.rb
+++ b/app/presenters/status_relationships_presenter.rb
@@ -4,7 +4,7 @@ class StatusRelationshipsPresenter
PINNABLE_VISIBILITIES = %w(public public_unlisted unlisted login private).freeze
attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map,
- :bookmarks_map, :filters_map, :emoji_reactions_map, :attributes_map
+ :bookmarks_map, :filters_map, :emoji_reactions_map, :attributes_map, :emoji_reaction_allows_map
def initialize(statuses, current_account_id = nil, **options)
@current_account_id = current_account_id
@@ -17,6 +17,7 @@ class StatusRelationshipsPresenter
@pins_map = {}
@filters_map = {}
@emoji_reactions_map = {}
+ @emoji_reaction_allows_map = nil
else
statuses = statuses.compact
status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq.compact
@@ -30,6 +31,7 @@ class StatusRelationshipsPresenter
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {})
@pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {})
@emoji_reactions_map = Status.emoji_reactions_map(status_ids, current_account_id).merge(options[:emoji_reactions_map] || {})
+ @emoji_reaction_allows_map = Status.emoji_reaction_allows_map(status_ids, current_account_id).merge(options[:emoji_reaction_allows_map] || {})
@attributes_map = options[:attributes_map] || {}
end
end
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index e53fb535bc..48aecb2994 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -15,6 +15,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
attribute :bookmarked, if: :current_user?
attribute :pinned, if: :pinnable?
attribute :reactions, if: :reactions?
+ attribute :expires_at, if: :will_expire?
has_many :filtered, serializer: REST::FilterResultSerializer, if: :current_user?
attribute :content, unless: :source_requested?
@@ -122,7 +123,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
end
def emoji_reactions
- object.emoji_reactions_grouped_by_name(current_user&.account)
+ show_emoji_reaction? ? object.emoji_reactions_grouped_by_name(current_user&.account, permitted_account_ids: emoji_reaction_permitted_account_ids) : []
end
def emoji_reactions_count
@@ -131,7 +132,17 @@ class REST::StatusSerializer < ActiveModel::Serializer
object.account.emoji_reaction_policy == :allow ? object.emoji_reactions_count : 0
else
- object.account.show_emoji_reaction?(current_user.account) ? object.emoji_reactions_count : 0
+ show_emoji_reaction? ? object.emoji_reactions_count : 0
+ end
+ end
+
+ def show_emoji_reaction?
+ if relationships
+ return true if relationships.emoji_reaction_allows_map.nil?
+
+ relationships.emoji_reaction_allows_map[object.account_id] || false
+ else
+ object.account.show_emoji_reaction?(current_user.account)
end
end
@@ -205,12 +216,24 @@ class REST::StatusSerializer < ActiveModel::Serializer
object.active_mentions.to_a.sort_by(&:id)
end
+ def will_expire?
+ object.scheduled_expiration_status.present?
+ end
+
+ def expires_at
+ object.scheduled_expiration_status.scheduled_at
+ end
+
private
def relationships
instance_options && instance_options[:relationships]
end
+ def emoji_reaction_permitted_account_ids
+ current_user.present? && instance_options && instance_options[:emoji_reaction_permitted_account_ids]&.permitted_account_ids
+ end
+
class ApplicationSerializer < ActiveModel::Serializer
attributes :name, :website
diff --git a/app/services/process_references_service.rb b/app/services/process_references_service.rb
index 0dbdc43560..9c8b3813f5 100644
--- a/app/services/process_references_service.rb
+++ b/app/services/process_references_service.rb
@@ -5,13 +5,14 @@ class ProcessReferencesService < BaseService
DOMAIN = ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
REFURL_EXP = /(RT|QT|BT|RN|RE)((:|;)?\s+|:|;)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
+ MAX_REFERENCES = 5
def call(status, reference_parameters, urls: nil)
@status = status
@reference_parameters = reference_parameters || []
@urls = urls || []
- old_references
+ @references_count = old_references.size
return unless added_references.size.positive? || removed_references.size.positive?
@@ -63,6 +64,9 @@ class ProcessReferencesService < BaseService
statuses.each do |status|
@added_objects << @status.reference_objects.new(target_status: status)
status.increment_count!(:status_referred_by_count)
+ @references_count += 1
+
+ break if @references_count >= MAX_REFERENCES
end
end
@@ -85,6 +89,7 @@ class ProcessReferencesService < BaseService
@status.reference_objects.where(target_status: statuses).destroy_all
statuses.each do |status|
status.decrement_count!(:status_referred_by_count)
+ @references_count -= 1
end
end
end
diff --git a/app/views/antennas/_antenna_fields.html.haml b/app/views/antennas/_antenna_fields.html.haml
index ac742d9d38..998aa5fdeb 100644
--- a/app/views/antennas/_antenna_fields.html.haml
+++ b/app/views/antennas/_antenna_fields.html.haml
@@ -8,57 +8,4 @@
= f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, include_blank: I18n.t('invites.expires_in_prompt')
.fields-row
- .fields-group.fields-row__column.fields-row__column-6
- = f.input :list, collection: lists, wrapper: :with_label, label_method: lambda { |list| list.title }, label: t('antennas.edit.list'), selected: f.object.list&.id, hint: false, include_blank: '[Insert to Home]'
- .fields-group.fields-row__column.fields-row__column-6
- = f.input :available, wrapper: :with_label, label: t('antennas.edit.available'), hint: false
-
-.fields-row
- = f.input :insert_feeds, wrapper: :with_label, label: t('antennas.edit.insert_feeds')
-.fields-row
- = f.input :stl, wrapper: :with_label, label: t('antennas.edit.stl'), hint: t('antennas.edit.stl_hint')
-
-%hr.spacer/
-%p.hint= t 'antennas.edit.hint'
-%hr.spacer/
-
-%h4= t('antennas.contexts.domain')
-%p.hint= t 'antennas.edit.domains_hint'
-
-.fields-row
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.domains_raw')
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :exclude_domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.exclude_domains_raw')
-
-%h4= t('antennas.contexts.account')
-%p.hint= t 'antennas.edit.accounts_hint'
-
-.fields-row
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.accounts_raw')
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :exclude_accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.exclude_accounts_raw')
-
-%h4= t('antennas.contexts.tag')
-
-.fields-row
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.tags_raw')
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :exclude_tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.exclude_tags_raw')
-
-%h4= t('antennas.contexts.keyword')
-%p.hint= t 'antennas.edit.keywords_hint'
-
-.fields-row
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.keywords_raw')
- .fields-row__column.fields-row__column-6.fields-group
- = f.input :exclude_keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.exclude_keywords_raw')
-
-%hr.spacer/
-.fields-group
- = f.input :with_media_only, wrapper: :with_label, label: t('antennas.edit.with_media_only'), hint: false
-.fields-group
- = f.input :ignore_reblog, wrapper: :with_label, label: t('antennas.edit.ignore_reblog'), hint: false
+ = f.input :available, wrapper: :with_label, label: t('antennas.edit.available'), hint: false
diff --git a/app/workers/activitypub/fetch_instance_info_worker.rb b/app/workers/activitypub/fetch_instance_info_worker.rb
index faf7401fc9..57cbd97d10 100644
--- a/app/workers/activitypub/fetch_instance_info_worker.rb
+++ b/app/workers/activitypub/fetch_instance_info_worker.rb
@@ -9,7 +9,6 @@ class ActivityPub::FetchInstanceInfoWorker
sidekiq_options queue: 'push', retry: 2
class Error < StandardError; end
- class GoneError < Error; end
class RequestError < Error; end
class DeadError < Error; end
@@ -68,9 +67,7 @@ class ActivityPub::FetchInstanceInfoWorker
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
body_to_json(response.body_with_limit)
- elsif response.code == 410
- raise ActivityPub::FetchInstanceInfoWorker::GoneError, "#{@instance.domain} is gone from the server"
- elsif response.code == 404
+ elsif [400, 401, 403, 404, 410].include?(response.code)
raise ActivityPub::FetchInstanceInfoWorker::DeadError, "Request for #{@instance.domain} returned HTTP #{response.code}"
else
raise ActivityPub::FetchInstanceInfoWorker::RequestError, "Request for #{@instance.domain} returned HTTP #{response.code}"
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index c00ed740a2..92179d9b8f 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -5,7 +5,7 @@ module Mastodon
module_function
def kmyblue_major
- 3
+ 4
end
def kmyblue_minor
diff --git a/package.json b/package.json
index 8f95f09fa4..5de05c53b6 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"@formatjs/intl-pluralrules": "^5.2.2",
"@gamestdio/websocket": "^0.3.2",
"@github/webauthn-json": "^2.1.1",
+ "@hello-pangea/dnd": "^16.3.0",
"@material-design-icons/svg": "^0.14.10",
"@rails/ujs": "^7.0.6",
"@reduxjs/toolkit": "^1.9.5",
@@ -100,7 +101,6 @@
"prop-types": "^15.8.1",
"punycode": "^2.3.0",
"react": "^18.2.0",
- "react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-hotkeys": "^1.1.4",
diff --git a/spec/controllers/api/v1/circles/accounts_controller_spec.rb b/spec/controllers/api/v1/circles/accounts_controller_spec.rb
new file mode 100644
index 0000000000..bddd8aa211
--- /dev/null
+++ b/spec/controllers/api/v1/circles/accounts_controller_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Circles::AccountsController do
+ render_views
+
+ let(:user) { Fabricate(:user) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+ let(:circle) { Fabricate(:circle, account: user.account) }
+ let(:follow) { Fabricate(:follow, target_account: user.account) }
+
+ before do
+ circle.accounts << follow.account
+ allow(controller).to receive(:doorkeeper_token) { token }
+ end
+
+ describe 'GET #index' do
+ let(:scopes) { 'read:lists' }
+
+ it 'returns http success' do
+ get :show, params: { circle_id: circle.id }
+
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ describe 'POST #create' do
+ let(:scopes) { 'write:lists' }
+ let(:bob) { Fabricate(:account, username: 'bob') }
+
+ context 'when the added account is followed' do
+ before do
+ bob.follow!(user.account)
+ post :create, params: { circle_id: circle.id, account_ids: [bob.id] }
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'adds account to the circle' do
+ expect(circle.accounts.include?(bob)).to be true
+ end
+ end
+
+ context 'when the added account has been sent a follow request' do
+ before do
+ bob.follow_requests.create!(target_account: user.account)
+ post :create, params: { circle_id: circle.id, account_ids: [bob.id] }
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(404)
+ end
+
+ it 'adds account to the circle' do
+ expect(circle.accounts.include?(bob)).to be false
+ end
+ end
+
+ context 'when the added account is not followed' do
+ before do
+ post :create, params: { circle_id: circle.id, account_ids: [bob.id] }
+ end
+
+ it 'returns http not found' do
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not add the account to the circle' do
+ expect(circle.accounts.include?(bob)).to be false
+ end
+ end
+ end
+
+ describe 'DELETE #destroy' do
+ let(:scopes) { 'write:lists' }
+
+ before do
+ delete :destroy, params: { circle_id: circle.id, account_ids: [circle.accounts.first.id] }
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'removes account from the circle' do
+ expect(circle.accounts.count).to eq 0
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/circles_controller_spec.rb b/spec/controllers/api/v1/circles_controller_spec.rb
new file mode 100644
index 0000000000..1aff55e65b
--- /dev/null
+++ b/spec/controllers/api/v1/circles_controller_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::CirclesController do
+ render_views
+
+ let(:user) { Fabricate(:user) }
+ let(:circle) { Fabricate(:circle, account: user.account) }
+
+ before do
+ allow(controller).to receive(:doorkeeper_token) { token }
+ end
+
+ context 'with a user context' do
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
+
+ describe 'GET #show' do
+ it 'returns http success' do
+ get :show, params: { id: circle.id }
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
+
+ context 'with the wrong user context' do
+ let(:other_user) { Fabricate(:user) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: other_user.id, scopes: 'read') }
+
+ describe 'GET #show' do
+ it 'returns http not found' do
+ get :show, params: { id: circle.id }
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ context 'without a user context' do
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read') }
+
+ describe 'GET #show' do
+ it 'returns http unprocessable entity' do
+ get :show, params: { id: circle.id }
+
+ expect(response).to have_http_status(422)
+ expect(response.headers['Link']).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/lists_controller_spec.rb b/spec/controllers/api/v1/lists_controller_spec.rb
new file mode 100644
index 0000000000..b54f3f70fa
--- /dev/null
+++ b/spec/controllers/api/v1/lists_controller_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::ListsController do
+ render_views
+
+ let(:user) { Fabricate(:user) }
+ let(:list) { Fabricate(:list, account: user.account) }
+
+ before do
+ allow(controller).to receive(:doorkeeper_token) { token }
+ end
+
+ context 'with a user context' do
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
+
+ describe 'GET #show' do
+ it 'returns http success' do
+ get :show, params: { id: list.id }
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
+
+ context 'with the wrong user context' do
+ let(:other_user) { Fabricate(:user) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: other_user.id, scopes: 'read') }
+
+ describe 'GET #show' do
+ it 'returns http not found' do
+ get :show, params: { id: list.id }
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ context 'without a user context' do
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read') }
+
+ describe 'GET #show' do
+ it 'returns http unprocessable entity' do
+ get :show, params: { id: list.id }
+
+ expect(response).to have_http_status(422)
+ expect(response.headers['Link']).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/models/tag_feed_spec.rb b/spec/models/tag_feed_spec.rb
index 6f5e1eb307..270797ccd8 100644
--- a/spec/models/tag_feed_spec.rb
+++ b/spec/models/tag_feed_spec.rb
@@ -66,5 +66,47 @@ describe TagFeed, type: :service do
results = described_class.new(tag_cats, nil).get(20)
expect(results).to include(status)
end
+
+ it 'public_unlisted post returns' do
+ status_tagged_with_cats.update(visibility: :public_unlisted)
+ results = described_class.new(tag_cats, nil).get(20)
+ expect(results).to include status_tagged_with_cats
+ end
+
+ it 'unlisted post not returns' do
+ status_tagged_with_cats.update(visibility: :unlisted)
+ results = described_class.new(tag_cats, nil).get(20)
+ expect(results).to_not include status_tagged_with_cats
+ end
+
+ it 'unlisted post not returns with account' do
+ status_tagged_with_cats.update(visibility: :unlisted)
+ results = described_class.new(tag_cats, account).get(20)
+ expect(results).to_not include status_tagged_with_cats
+ end
+
+ it 'unlisted/public_searchability post returns' do
+ status_tagged_with_cats.update(visibility: :unlisted, searchability: :public)
+ results = described_class.new(tag_cats, nil).get(20)
+ expect(results).to include status_tagged_with_cats
+ end
+
+ it 'unlisted/public_searchability post returns with account' do
+ status_tagged_with_cats.update(visibility: :unlisted, searchability: :public)
+ results = described_class.new(tag_cats, account).get(20)
+ expect(results).to include status_tagged_with_cats
+ end
+
+ it 'private post not returns' do
+ status_tagged_with_cats.update(visibility: :private, searchability: :public)
+ results = described_class.new(tag_cats, nil).get(20)
+ expect(results).to_not include status_tagged_with_cats
+ end
+
+ it 'private post not returns with account' do
+ status_tagged_with_cats.update(visibility: :private, searchability: :public)
+ results = described_class.new(tag_cats, account).get(20)
+ expect(results).to_not include status_tagged_with_cats
+ end
end
end
diff --git a/spec/services/delivery_antenna_service_spec.rb b/spec/services/delivery_antenna_service_spec.rb
index aa63aed74c..26c23eb930 100644
--- a/spec/services/delivery_antenna_service_spec.rb
+++ b/spec/services/delivery_antenna_service_spec.rb
@@ -266,8 +266,8 @@ RSpec.describe DeliveryAntennaService, type: :service do
end
context 'when multiple antennas insert list with keyword' do
- let!(:antenna) { antenna_with_keyword(bob, 'body', insert_feeds: true, list: list(bob).id) }
- let!(:empty_antenna) { antenna_with_keyword(tom, 'body', insert_feeds: true, list: list(tom).id) }
+ let!(:antenna) { antenna_with_keyword(bob, 'body', insert_feeds: true, list: list(bob)) }
+ let!(:empty_antenna) { antenna_with_keyword(tom, 'body', insert_feeds: true, list: list(tom)) }
it 'detecting antenna' do
expect(antenna_feed_of(antenna)).to include status.id
diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb
index c15253c879..65db23214a 100644
--- a/spec/services/fan_out_on_write_service_spec.rb
+++ b/spec/services/fan_out_on_write_service_spec.rb
@@ -7,9 +7,10 @@ RSpec.describe FanOutOnWriteService, type: :service do
let(:last_active_at) { Time.now.utc }
let(:searchability) { 'public' }
+ let(:dissubscribable) { false }
let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, text: 'Hello @bob #hoge') }
- let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at).account }
+ let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { dissubscribable: dissubscribable }).account }
let!(:bob) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account }
let!(:tom) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:ohagi) { Fabricate(:user, current_sign_in_at: last_active_at).account }
@@ -100,6 +101,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is not added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to_not include status.id
+ end
+ end
end
context 'with STL antenna' do
@@ -110,6 +119,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ end
+ end
end
context 'with LTL antenna' do
@@ -120,6 +137,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ end
+ end
end
end
@@ -238,6 +263,89 @@ RSpec.describe FanOutOnWriteService, type: :service do
end
end
+ context 'when status is public_unlisted' do
+ let(:visibility) { 'public_unlisted' }
+
+ it 'is added to the home feed of its author' do
+ expect(home_feed_of(alice)).to include status.id
+ end
+
+ it 'is added to the home feed of a follower' do
+ expect(home_feed_of(bob)).to include status.id
+ expect(home_feed_of(tom)).to include status.id
+ end
+
+ it 'is broadcast publicly' do
+ expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything)
+ expect(redis).to have_received(:publish).with('timeline:public:local', anything)
+ expect(redis).to_not have_received(:publish).with('timeline:public', anything)
+ end
+
+ context 'with list' do
+ let!(:list) { list_with_account(bob, alice) }
+ let!(:empty_list) { list_with_account(ohagi, bob) }
+
+ it 'is added to the list feed of list follower' do
+ expect(list_feed_of(list)).to include status.id
+ expect(list_feed_of(empty_list)).to_not include status.id
+ end
+ end
+
+ context 'with antenna' do
+ let!(:antenna) { antenna_with_account(bob, alice) }
+ let!(:empty_antenna) { antenna_with_account(tom, bob) }
+
+ it 'is added to the antenna feed of antenna follower' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ expect(antenna_feed_of(empty_antenna)).to_not include status.id
+ end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is not added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to_not include status.id
+ end
+ end
+ end
+
+ context 'with STL antenna' do
+ let!(:antenna) { antenna_with_options(bob, stl: true) }
+ let!(:empty_antenna) { antenna_with_options(tom) }
+
+ it 'is added to the antenna feed of antenna follower' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ expect(antenna_feed_of(empty_antenna)).to_not include status.id
+ end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ end
+ end
+ end
+
+ context 'with LTL antenna' do
+ let!(:antenna) { antenna_with_options(bob, ltl: true) }
+ let!(:empty_antenna) { antenna_with_options(tom) }
+
+ it 'is added to the antenna feed of antenna follower' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ expect(antenna_feed_of(empty_antenna)).to_not include status.id
+ end
+
+ context 'when dissubscribable is true' do
+ let(:dissubscribable) { true }
+
+ it 'is added to the antenna feed' do
+ expect(antenna_feed_of(antenna)).to include status.id
+ end
+ end
+ end
+ end
+
context 'when status is unlisted' do
let(:visibility) { 'unlisted' }
@@ -255,6 +363,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(redis).to_not have_received(:publish).with('timeline:public', anything)
end
+ context 'with searchability private' do
+ let(:searchability) { 'private' }
+
+ it 'is not broadcast to the hashtag stream' do
+ expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge', anything)
+ expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge:local', anything)
+ end
+ end
+
context 'with list' do
let!(:list) { list_with_account(bob, alice) }
let!(:empty_list) { list_with_account(ohagi, bob) }
diff --git a/yarn.lock b/yarn.lock
index 95bea8c933..0c5eddba7c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1098,7 +1098,7 @@
dependencies:
regenerator-runtime "^0.12.0"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.22.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
@@ -1458,6 +1458,19 @@
resolved "https://registry.yarnpkg.com/@github/webauthn-json/-/webauthn-json-2.1.1.tgz#648e63fc28050917d2882cc2b27817a88cb420fc"
integrity sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==
+"@hello-pangea/dnd@^16.3.0":
+ version "16.3.0"
+ resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.3.0.tgz#3776212f812df4e8e69c42831ec8ab7ff3a087d6"
+ integrity sha512-RYQ/K8shtJoyNPvFWz0gfXIK7HF3P3mL9UZFGMuHB0ljRSXVgMjVFI/FxcZmakMzw6tO7NflWLriwTNBow/4vw==
+ dependencies:
+ "@babel/runtime" "^7.22.5"
+ css-box-model "^1.2.1"
+ memoize-one "^6.0.0"
+ raf-schd "^4.0.3"
+ react-redux "^8.1.1"
+ redux "^4.2.1"
+ use-memo-one "^1.1.3"
+
"@humanwhocodes/config-array@^0.11.11":
version "0.11.11"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844"
@@ -2190,7 +2203,7 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
-"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
+"@types/hoist-non-react-statics@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
@@ -2403,16 +2416,6 @@
dependencies:
react-overlays "*"
-"@types/react-redux@^7.1.20":
- version "7.1.25"
- resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
- integrity sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==
- dependencies:
- "@types/hoist-non-react-statics" "^3.3.0"
- "@types/react" "*"
- hoist-non-react-statics "^3.3.0"
- redux "^4.0.0"
-
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
@@ -4456,7 +4459,7 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
-css-box-model@^1.2.0:
+css-box-model@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
@@ -8368,11 +8371,6 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
-memoize-one@^5.1.1:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
- integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
-
memoize-one@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
@@ -9924,7 +9922,7 @@ quick-lru@^5.1.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
-raf-schd@^4.0.2:
+raf-schd@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
@@ -9966,19 +9964,6 @@ raw-body@2.5.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
-react-beautiful-dnd@^13.1.1:
- version "13.1.1"
- resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
- integrity sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==
- dependencies:
- "@babel/runtime" "^7.9.2"
- css-box-model "^1.2.0"
- memoize-one "^5.1.1"
- raf-schd "^4.0.2"
- react-redux "^7.2.0"
- redux "^4.0.4"
- use-memo-one "^1.1.1"
-
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -10060,7 +10045,7 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-react-is@^17.0.1, react-is@^17.0.2:
+react-is@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
@@ -10108,19 +10093,7 @@ react-redux-loading-bar@^5.0.4:
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.4"
-react-redux@^7.2.0:
- version "7.2.9"
- resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d"
- integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==
- dependencies:
- "@babel/runtime" "^7.15.4"
- "@types/react-redux" "^7.1.20"
- hoist-non-react-statics "^3.3.2"
- loose-envify "^1.4.0"
- prop-types "^15.7.2"
- react-is "^17.0.2"
-
-react-redux@^8.0.4:
+react-redux@^8.0.4, react-redux@^8.1.1:
version "8.1.2"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.2.tgz#9076bbc6b60f746659ad6d51cb05de9c5e1e9188"
integrity sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==
@@ -10378,7 +10351,7 @@ redux-thunk@^2.4.2:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
-redux@^4.0.0, redux@^4.0.4, redux@^4.2.1:
+redux@^4.0.0, redux@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
@@ -12312,7 +12285,7 @@ use-latest@^1.2.1:
dependencies:
use-isomorphic-layout-effect "^1.1.1"
-use-memo-one@^1.1.1:
+use-memo-one@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==