Merge branch 'kb_development' into kb_migration

This commit is contained in:
KMY 2023-09-15 10:14:29 +09:00
commit ed6acbf542
32 changed files with 509 additions and 275 deletions

View file

@ -5,7 +5,6 @@ class AntennasController < ApplicationController
before_action :authenticate_user!
before_action :set_antenna, only: [:edit, :update, :destroy]
before_action :set_lists, only: [:new, :edit]
before_action :set_body_classes
before_action :set_cache_headers
@ -54,16 +53,12 @@ class AntennasController < ApplicationController
@antenna = current_account.antennas.find(params[:id])
end
def set_lists
@lists = current_account.owned_lists
end
def resource_params
params.require(:antenna).permit(:title, :list, :available, :insert_feeds, :stl, :expires_in, :with_media_only, :ignore_reblog, :keywords_raw, :exclude_keywords_raw, :domains_raw, :exclude_domains_raw, :accounts_raw, :exclude_accounts_raw, :tags_raw, :exclude_tags_raw)
params.require(:antenna).permit(:title, :available, :expires_in)
end
def thin_resource_params
params.require(:antenna).permit(:title, :list)
params.require(:antenna).permit(:title)
end
def set_body_classes

View file

@ -9,7 +9,9 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
def index
cache_if_unauthenticated!
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -7,7 +7,9 @@ class Api::V1::BookmarksController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -7,7 +7,9 @@ class Api::V1::EmojiReactionsController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -7,7 +7,9 @@ class Api::V1::FavouritesController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -9,7 +9,9 @@ class Api::V1::Statuses::ReferredByStatusesController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -9,11 +9,13 @@ class Api::V1::Timelines::HomeController < Api::BaseController
with_read_replica do
@statuses = load_statuses
@relationships = StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
@emoji_reactions = EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
render json: @statuses,
each_serializer: REST::StatusSerializer,
relationships: @relationships,
emoji_reaction_permitted_account_ids: @emoji_reactions,
status: account_home_feed.regenerating? ? 206 : 200
end

View file

@ -7,7 +7,9 @@ class Api::V1::Timelines::PublicController < Api::BaseController
def show
cache_if_unauthenticated!
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -8,7 +8,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
def show
cache_if_unauthenticated!
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
emoji_reaction_permitted_account_ids: EmojiReactionAccountsPresenter.new(@statuses, current_user&.account_id)
end
private

View file

@ -585,6 +585,10 @@ class Status extends ImmutablePureComponent {
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
const expanded = !status.get('hidden')
const withLimited = status.get('visibility_ex') === 'limited' && status.get('limited_scope') ? <span className='status__visibility-icon'><Icon id='get-pocket' title='Limited' /></span> : null;
const withReference = status.get('status_references_count') > 0 ? <span className='status__visibility-icon'><Icon id='link' title='Reference' /></span> : null;
const withExpiration = status.get('expires_at') ? <span className='status__visibility-icon'><Icon id='clock-o' title='Expiration' /></span> : null;
return (
<HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility_ex')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef} data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined}>
@ -596,6 +600,9 @@ class Status extends ImmutablePureComponent {
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div onClick={this.handleClick} className='status__info'>
<a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
{withReference}
{withExpiration}
{withLimited}
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
</a>

View file

@ -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';

View file

@ -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

View file

@ -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

View file

@ -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?

View file

@ -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 = []

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}"

View file

@ -5,7 +5,7 @@ module Mastodon
module_function
def kmyblue_major
3
4
end
def kmyblue_minor

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) }

View file

@ -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==