From e82d84469e03a443b5440806903ba3be614f4ab8 Mon Sep 17 00:00:00 2001 From: jsgoldstein Date: Thu, 24 Aug 2023 10:40:04 -0400 Subject: [PATCH 001/182] Add new public status index (#26344) Co-authored-by: Eugen Rochko Co-authored-by: Claire --- app/chewy/accounts_index.rb | 2 +- app/chewy/public_statuses_index.rb | 50 +++++++++++++ app/chewy/statuses_index.rb | 22 +++--- .../api/v1/accounts/credentials_controller.rb | 1 + .../settings/privacy_controller.rb | 2 +- .../public_statuses_index_importer.rb | 41 ++++++++++ app/lib/search_query_transformer.rb | 49 +++++++++--- app/lib/vacuum/statuses_vacuum.rb | 9 ++- app/models/account.rb | 1 + .../concerns/account_statuses_search.rb | 44 +++++++++++ app/models/concerns/status_search_concern.rb | 54 +++++++++++++ app/models/status.rb | 37 ++------- .../activitypub/actor_serializer.rb | 8 +- app/services/batched_remove_status_service.rb | 5 +- app/services/search_service.rb | 32 +++----- app/services/statuses_search_service.rb | 75 +++++++++++++++++++ app/views/settings/privacy/show.html.haml | 3 + .../add_to_public_statuses_index_worker.rb | 15 ++++ ...emove_from_public_statuses_index_worker.rb | 15 ++++ app/workers/scheduler/indexing_scheduler.rb | 2 +- config/locales/simple_form.en.yml | 2 + lib/mastodon/cli/search.rb | 1 + spec/chewy/public_statuses_index_spec.rb | 31 ++++++++ .../public_statuses_index_importer_spec.rb | 16 ++++ spec/lib/search_query_transformer_spec.rb | 4 +- .../concerns/account_statuses_search_spec.rb | 66 ++++++++++++++++ ...dd_to_public_statuses_index_worker_spec.rb | 42 +++++++++++ ..._from_public_statuses_index_worker_spec.rb | 42 +++++++++++ 28 files changed, 584 insertions(+), 87 deletions(-) create mode 100644 app/chewy/public_statuses_index.rb create mode 100644 app/lib/importer/public_statuses_index_importer.rb create mode 100644 app/models/concerns/account_statuses_search.rb create mode 100644 app/models/concerns/status_search_concern.rb create mode 100644 app/services/statuses_search_service.rb create mode 100644 app/workers/add_to_public_statuses_index_worker.rb create mode 100644 app/workers/remove_from_public_statuses_index_worker.rb create mode 100644 spec/chewy/public_statuses_index_spec.rb create mode 100644 spec/lib/importer/public_statuses_index_importer_spec.rb create mode 100644 spec/models/concerns/account_statuses_search_spec.rb create mode 100644 spec/workers/add_to_public_statuses_index_worker_spec.rb create mode 100644 spec/workers/remove_from_public_statuses_index_worker_spec.rb diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 1f8571c09d..61e3399aa8 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -62,6 +62,6 @@ class AccountsIndex < Chewy::Index field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } - field(:text, type: 'text', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } + field(:text, type: 'text', analyzer: 'whitespace', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } end end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb new file mode 100644 index 0000000000..1fad5de3a1 --- /dev/null +++ b/app/chewy/public_statuses_index.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class PublicStatusesIndex < Chewy::Index + settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { + filter: { + english_stop: { + type: 'stop', + stopwords: '_english_', + }, + + english_stemmer: { + type: 'stemmer', + language: 'english', + }, + + english_possessive_stemmer: { + type: 'stemmer', + language: 'possessive_english', + }, + }, + + analyzer: { + content: { + tokenizer: 'uax_url_email', + filter: %w( + english_possessive_stemmer + lowercase + asciifolding + cjk_width + english_stop + english_stemmer + ), + }, + }, + } + + index_scope ::Status.unscoped + .kept + .indexable + .includes(:media_attachments, :preloadable_poll, :preview_cards) + + root date_detection: false do + field(:id, type: 'keyword') + field(:account_id, type: 'long') + field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:language, type: 'keyword') + field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) + field(:created_at, type: 'date') + end +end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 9f680efa52..130f8801df 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -1,23 +1,24 @@ # frozen_string_literal: true class StatusesIndex < Chewy::Index - include FormattingHelper - settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { filter: { english_stop: { type: 'stop', stopwords: '_english_', }, + english_stemmer: { type: 'stemmer', language: 'english', }, + english_possessive_stemmer: { type: 'stemmer', language: 'possessive_english', }, }, + analyzer: { content: { tokenizer: 'uax_url_email', @@ -35,7 +36,7 @@ class StatusesIndex < Chewy::Index # We do not use delete_if option here because it would call a method that we # expect to be called with crutches without crutches, causing n+1 queries - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll, :preview_cards) crutch :mentions do |collection| data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) @@ -63,13 +64,12 @@ class StatusesIndex < Chewy::Index end root date_detection: false do - field :id, type: 'long' - field :account_id, type: 'long' - - field :text, type: 'text', value: ->(status) { status.searchable_text } do - field :stemmed, type: 'text', analyzer: 'content' - end - - field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) } + field(:id, type: 'keyword') + field(:account_id, type: 'long') + field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) + field(:language, type: 'keyword') + field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) + field(:created_at, type: 'date') end end diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 7c7d70fd32..76ba758245 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -30,6 +30,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController :bot, :discoverable, :hide_collections, + :indexable, fields_attributes: [:name, :value] ) end diff --git a/app/controllers/settings/privacy_controller.rb b/app/controllers/settings/privacy_controller.rb index c2648eedd8..1102c89fad 100644 --- a/app/controllers/settings/privacy_controller.rb +++ b/app/controllers/settings/privacy_controller.rb @@ -18,7 +18,7 @@ class Settings::PrivacyController < Settings::BaseController private def account_params - params.require(:account).permit(:discoverable, :unlocked, :show_collections, settings: UserSettings.keys) + params.require(:account).permit(:discoverable, :unlocked, :indexable, :show_collections, settings: UserSettings.keys) end def set_account diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb new file mode 100644 index 0000000000..8e36e36f90 --- /dev/null +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class Importer::PublicStatusesIndexImporter < Importer::BaseImporter + def import! + indexable_statuses_scope.find_in_batches(batch_size: @batch_size) do |batch| + in_work_unit(batch.map(&:status_id)) do |status_ids| + bulk = ActiveRecord::Base.connection_pool.with_connection do + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + end + + indexed = 0 + deleted = 0 + + bulk.map! do |entry| + if entry[:index] + indexed += 1 + else + deleted += 1 + end + entry + end + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [indexed, deleted] + end + end + + wait! + end + + private + + def index + PublicStatusesIndex + end + + def indexable_statuses_scope + Status.indexable.select('"statuses"."id", COALESCE("statuses"."reblog_of_id", "statuses"."id") AS status_id') + end +end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index aef05e9d9d..dad99cbd2d 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -36,7 +36,7 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_filter(clause) case clause when PrefixClause - { term: { clause.filter => clause.term } } + { clause.type => { clause.filter => clause.term } } else raise "Unexpected clause type: #{clause}" end @@ -47,12 +47,10 @@ class SearchQueryTransformer < Parslet::Transform class << self def symbol(str) case str - when '+' + when '+', nil :must when '-' :must_not - when nil - :should else raise "Unknown operator: #{str}" end @@ -81,23 +79,52 @@ class SearchQueryTransformer < Parslet::Transform end class PrefixClause - attr_reader :filter, :operator, :term + attr_reader :type, :filter, :operator, :term def initialize(prefix, term) @operator = :filter + case prefix + when 'has', 'is' + @filter = :properties + @type = :term + @term = term + when 'language' + @filter = :language + @type = :term + @term = term when 'from' @filter = :account_id - - username, domain = term.gsub(/\A@/, '').split('@') - domain = nil if TagManager.instance.local_domain?(domain) - account = Account.find_remote!(username, domain) - - @term = account.id + @type = :term + @term = account_id_from_term(term) + when 'before' + @filter = :created_at + @type = :range + @term = { lt: term } + when 'after' + @filter = :created_at + @type = :range + @term = { gt: term } + when 'during' + @filter = :created_at + @type = :range + @term = { gte: term, lte: term } else raise Mastodon::SyntaxError end end + + private + + def account_id_from_term(term) + username, domain = term.gsub(/\A@/, '').split('@') + domain = nil if TagManager.instance.local_domain?(domain) + account = Account.find_remote(username, domain) + + # If the account is not found, we want to return empty results, so return + # an ID that does not exist + account&.id || -1 + end end rule(clause: subtree(:clause)) do diff --git a/app/lib/vacuum/statuses_vacuum.rb b/app/lib/vacuum/statuses_vacuum.rb index 28c087b1c2..ad1de07380 100644 --- a/app/lib/vacuum/statuses_vacuum.rb +++ b/app/lib/vacuum/statuses_vacuum.rb @@ -20,7 +20,10 @@ class Vacuum::StatusesVacuum statuses.direct_visibility .includes(mentions: :account) .find_each(&:unlink_from_conversations!) - remove_from_search_index(statuses.ids) if Chewy.enabled? + if Chewy.enabled? + remove_from_index(statuses.ids, 'chewy:queue:StatusesIndex') + remove_from_index(statuses.ids, 'chewy:queue:PublicStatusesIndex') + end # Foreign keys take care of most associated records for us. # Media attachments will be orphaned. @@ -38,7 +41,7 @@ class Vacuum::StatusesVacuum Mastodon::Snowflake.id_at(@retention_period.ago, with_random: false) end - def remove_from_search_index(status_ids) - with_redis { |redis| redis.sadd('chewy:queue:StatusesIndex', status_ids) } + def remove_from_index(status_ids, index) + with_redis { |redis| redis.sadd(index, status_ids) } end end diff --git a/app/models/account.rb b/app/models/account.rb index b1cb9eb5db..244f3da83d 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -82,6 +82,7 @@ class Account < ApplicationRecord include DomainMaterializable include AccountMerging include AccountSearch + include AccountStatusesSearch enum protocol: { ostatus: 0, activitypub: 1 } enum suspension_origin: { local: 0, remote: 1 }, _prefix: true diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb new file mode 100644 index 0000000000..563a871051 --- /dev/null +++ b/app/models/concerns/account_statuses_search.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module AccountStatusesSearch + extend ActiveSupport::Concern + + included do + after_update_commit :enqueue_update_public_statuses_index, if: :saved_change_to_indexable? + after_destroy_commit :enqueue_remove_from_public_statuses_index, if: :indexable? + end + + def enqueue_update_public_statuses_index + if indexable? + enqueue_add_to_public_statuses_index + else + enqueue_remove_from_public_statuses_index + end + end + + def enqueue_add_to_public_statuses_index + return unless Chewy.enabled? + + AddToPublicStatusesIndexWorker.perform_async(id) + end + + def enqueue_remove_from_public_statuses_index + return unless Chewy.enabled? + + RemoveFromPublicStatusesIndexWorker.perform_async(id) + end + + def add_to_public_statuses_index! + return unless Chewy.enabled? + + statuses.indexable.find_in_batches do |batch| + PublicStatusesIndex.import(query: batch) + end + end + + def remove_from_public_statuses_index! + return unless Chewy.enabled? + + PublicStatusesIndex.filter(term: { account_id: id }).delete_all + end +end diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status_search_concern.rb new file mode 100644 index 0000000000..21048b5682 --- /dev/null +++ b/app/models/concerns/status_search_concern.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module StatusSearchConcern + extend ActiveSupport::Concern + + included do + scope :indexable, -> { without_reblogs.where(visibility: :public).joins(:account).where(account: { indexable: true }) } + end + + def searchable_by(preloaded = nil) + ids = [] + + ids << account_id if local? + + if preloaded.nil? + ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) + ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) + ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) + ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) + ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? + else + ids += preloaded.mentions[id] || [] + ids += preloaded.favourites[id] || [] + ids += preloaded.reblogs[id] || [] + ids += preloaded.bookmarks[id] || [] + ids += preloaded.votes[id] || [] + end + + ids.uniq + end + + def searchable_text + [ + spoiler_text, + FormattingHelper.extract_status_plain_text(self), + preloadable_poll&.options&.join("\n\n"), + ordered_media_attachments.map(&:description).join("\n\n"), + ].compact.join("\n\n") + end + + def searchable_properties + [].tap do |properties| + properties << 'image' if ordered_media_attachments.any?(&:image?) + properties << 'video' if ordered_media_attachments.any?(&:video?) + properties << 'audio' if ordered_media_attachments.any?(&:audio?) + properties << 'media' if with_media? + properties << 'poll' if with_poll? + properties << 'link' if with_preview_card? + properties << 'embed' if preview_cards.any?(&:video?) + properties << 'sensitive' if sensitive? + properties << 'reply' if reply? + end + end +end diff --git a/app/models/status.rb b/app/models/status.rb index 86fd8334a2..760b8ec33e 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -37,6 +37,7 @@ class Status < ApplicationRecord include StatusSnapshotConcern include RateLimitable include StatusSafeReblogInsert + include StatusSearchConcern rate_limit by: :account, family: :statuses @@ -47,6 +48,7 @@ class Status < ApplicationRecord attr_accessor :override_timestamps update_index('statuses', :proper) + update_index('public_statuses', :proper) enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, _suffix: :visibility @@ -165,37 +167,6 @@ class Status < ApplicationRecord "v3:#{super}" end - def searchable_by(preloaded = nil) - ids = [] - - ids << account_id if local? - - if preloaded.nil? - ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) - ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) - ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) - ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) - ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? - else - ids += preloaded.mentions[id] || [] - ids += preloaded.favourites[id] || [] - ids += preloaded.reblogs[id] || [] - ids += preloaded.bookmarks[id] || [] - ids += preloaded.votes[id] || [] - end - - ids.uniq - end - - def searchable_text - [ - spoiler_text, - FormattingHelper.extract_status_plain_text(self), - preloadable_poll ? preloadable_poll.options.join("\n\n") : nil, - ordered_media_attachments.map(&:description).join("\n\n"), - ].compact.join("\n\n") - end - def to_log_human_identifier account.acct end @@ -270,6 +241,10 @@ class Status < ApplicationRecord preview_cards.any? end + def with_poll? + preloadable_poll.present? + end + def non_sensitive_with_media? !sensitive? && with_media? end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index 4998d00399..31f39954fb 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -8,13 +8,13 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer context_extensions :manually_approves_followers, :featured, :also_known_as, :moved_to, :property_value, :discoverable, :olm, :suspended, - :memorial + :memorial, :indexable attributes :id, :type, :following, :followers, :inbox, :outbox, :featured, :featured_tags, :preferred_username, :name, :summary, :url, :manually_approves_followers, - :discoverable, :published, :memorial + :discoverable, :indexable, :published, :memorial has_one :public_key, serializer: ActivityPub::PublicKeySerializer @@ -99,6 +99,10 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer object.suspended? ? false : (object.discoverable || false) end + def indexable + object.suspended? ? false : (object.indexable || false) + end + def name object.suspended? ? object.username : (object.display_name.presence || object.username) end diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index f5cb339cdf..c54cc1d350 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -35,7 +35,10 @@ class BatchedRemoveStatusService < BaseService # Since we skipped all callbacks, we also need to manually # deindex the statuses - Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) if Chewy.enabled? + if Chewy.enabled? + Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) + Chewy.strategy.current.update(PublicStatusesIndex, statuses_and_reblogs) + end return if options[:skip_side_effects] diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 30937471bd..4e1e7ea26e 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -39,25 +39,15 @@ class SearchService < BaseService end def perform_statuses_search! - definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id })) - - definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present? - - if @options[:min_id].present? || @options[:max_id].present? - range = {} - range[:gt] = @options[:min_id].to_i if @options[:min_id].present? - range[:lt] = @options[:max_id].to_i if @options[:max_id].present? - definition = definition.filter(range: { id: range }) - end - - results = definition.limit(@limit).offset(@offset).objects.compact - account_ids = results.map(&:account_id) - account_domains = results.map(&:account_domain) - preloaded_relations = @account.relations_map(account_ids, account_domains) - - results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } - rescue Faraday::ConnectionFailed, Parslet::ParseFailed - [] + StatusesSearchService.new.call( + @query, + @account, + limit: @limit, + offset: @offset, + account_id: @options[:account_id], + min_id: @options[:min_id], + max_id: @options[:max_id] + ) end def perform_hashtags_search! @@ -114,8 +104,4 @@ class SearchService < BaseService def statuses_search? @options[:type].blank? || @options[:type] == 'statuses' end - - def parsed_query - SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) - end end diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb new file mode 100644 index 0000000000..21d6b71b7d --- /dev/null +++ b/app/services/statuses_search_service.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +class StatusesSearchService < BaseService + def call(query, account = nil, options = {}) + @query = query&.strip + @account = account + @options = options + @limit = options[:limit].to_i + @offset = options[:offset].to_i + + status_search_results + end + + private + + def status_search_results + definition = parsed_query.apply( + StatusesIndex.filter( + bool: { + should: [ + publicly_searchable, + non_publicly_searchable, + ], + + minimum_should_match: 1, + } + ) + ) + + # This is the best way to submit identical queries to multi-indexes though chewy + definition.instance_variable_get(:@parameters)[:indices].value[:indices] << PublicStatusesIndex + + results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact + account_ids = results.map(&:account_id) + account_domains = results.map(&:account_domain) + preloaded_relations = @account.relations_map(account_ids, account_domains) + + results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } + rescue Faraday::ConnectionFailed, Parslet::ParseFailed + [] + end + + def publicly_searchable + { + bool: { + must_not: { + exists: { + field: 'searchable_by', + }, + }, + }, + } + end + + def non_publicly_searchable + { + bool: { + must: [ + { + exists: { + field: 'searchable_by', + }, + }, + { + term: { searchable_by: @account.id }, + }, + ], + }, + } + end + + def parsed_query + SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) + end +end diff --git a/app/views/settings/privacy/show.html.haml b/app/views/settings/privacy/show.html.haml index ce31e60f06..3c14382587 100644 --- a/app/views/settings/privacy/show.html.haml +++ b/app/views/settings/privacy/show.html.haml @@ -24,6 +24,9 @@ %p.lead= t('privacy.search_hint_html') + .fields-group + = f.input :indexable, as: :boolean, wrapper: :with_label + = f.simple_fields_for :settings, current_user.settings do |ff| .fields-group = ff.input :indexable, wrapper: :with_label diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb new file mode 100644 index 0000000000..409e5e7086 --- /dev/null +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddToPublicStatusesIndexWorker + include Sidekiq::Worker + + def perform(account_id) + account = Account.find(account_id) + + return unless account.indexable? + + account.add_to_public_statuses_index! + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/remove_from_public_statuses_index_worker.rb b/app/workers/remove_from_public_statuses_index_worker.rb new file mode 100644 index 0000000000..e108a5c209 --- /dev/null +++ b/app/workers/remove_from_public_statuses_index_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class RemoveFromPublicStatusesIndexWorker + include Sidekiq::Worker + + def perform(account_id) + account = Account.find(account_id) + + return if account.indexable? + + account.remove_from_public_statuses_index! + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 2868a3b715..6c770d5a8f 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -23,6 +23,6 @@ class Scheduler::IndexingScheduler end def indexes - [AccountsIndex, TagsIndex, StatusesIndex] + [AccountsIndex, TagsIndex, PublicStatusesIndex, StatusesIndex] end end diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 443b7617ff..efda7b778b 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -6,6 +6,7 @@ en: discoverable: Your public posts and profile may be featured or recommended in various areas of Mastodon and your profile may be suggested to other users. display_name: Your full name or your fun name. fields: Your homepage, pronouns, age, anything you want. + indexable: Your public posts may appear in search results on Mastodon. People who have interacted with your posts may be able to search them regardless. note: 'You can @mention other people or #hashtags.' show_collections: People will be able to browse through your follows and followers. People that you follow will see that you follow them regardless. unlocked: People will be able to follow you without requesting approval. Uncheck if you want to review follow requests and chose whether to accept or reject new followers. @@ -143,6 +144,7 @@ en: fields: name: Label value: Content + indexable: Include public posts in search results show_collections: Show follows and followers on profile unlocked: Automatically accept new followers account_alias: diff --git a/lib/mastodon/cli/search.rb b/lib/mastodon/cli/search.rb index 41862b5b6b..481e01d8e7 100644 --- a/lib/mastodon/cli/search.rb +++ b/lib/mastodon/cli/search.rb @@ -10,6 +10,7 @@ module Mastodon::CLI InstancesIndex, AccountsIndex, TagsIndex, + PublicStatusesIndex, StatusesIndex, ].freeze diff --git a/spec/chewy/public_statuses_index_spec.rb b/spec/chewy/public_statuses_index_spec.rb new file mode 100644 index 0000000000..2f93d0ff02 --- /dev/null +++ b/spec/chewy/public_statuses_index_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PublicStatusesIndex do + describe 'Searching the index' do + before do + mock_elasticsearch_response(described_class, raw_response) + end + + it 'returns results from a query' do + results = described_class.query(match: { name: 'status' }) + + expect(results).to eq [] + end + end + + def raw_response + { + took: 3, + hits: { + hits: [ + { + _id: '0', + _score: 1.6375021, + }, + ], + }, + } + end +end diff --git a/spec/lib/importer/public_statuses_index_importer_spec.rb b/spec/lib/importer/public_statuses_index_importer_spec.rb new file mode 100644 index 0000000000..bc7c038a97 --- /dev/null +++ b/spec/lib/importer/public_statuses_index_importer_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Importer::PublicStatusesIndexImporter do + describe 'import!' do + let(:pool) { Concurrent::FixedThreadPool.new(5) } + let(:importer) { described_class.new(batch_size: 123, executor: pool) } + + before { Fabricate(:status, account: Fabricate(:account, indexable: true)) } + + it 'indexes relevant statuses' do + expect { importer.import! }.to update_index(PublicStatusesIndex) + end + end +end diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 1095334695..953f9acb2f 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -9,8 +9,8 @@ describe SearchQueryTransformer do it 'sets attributes' do transformer = described_class.new.apply(parser) - expect(transformer.should_clauses.first).to be_a(SearchQueryTransformer::TermClause) - expect(transformer.must_clauses.first).to be_nil + expect(transformer.should_clauses.first).to be_nil + expect(transformer.must_clauses.first).to be_a(SearchQueryTransformer::TermClause) expect(transformer.must_not_clauses.first).to be_nil expect(transformer.filter_clauses.first).to be_nil end diff --git a/spec/models/concerns/account_statuses_search_spec.rb b/spec/models/concerns/account_statuses_search_spec.rb new file mode 100644 index 0000000000..46362936f4 --- /dev/null +++ b/spec/models/concerns/account_statuses_search_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AccountStatusesSearch do + let(:account) { Fabricate(:account, indexable: indexable) } + + before do + allow(Chewy).to receive(:enabled?).and_return(true) + end + + describe '#enqueue_update_public_statuses_index' do + before do + allow(account).to receive(:enqueue_add_to_public_statuses_index) + allow(account).to receive(:enqueue_remove_from_public_statuses_index) + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'enqueues add_to_public_statuses_index and not to remove_from_public_statuses_index' do + account.enqueue_update_public_statuses_index + expect(account).to have_received(:enqueue_add_to_public_statuses_index) + expect(account).to_not have_received(:enqueue_remove_from_public_statuses_index) + end + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'enqueues remove_from_public_statuses_index and not to add_to_public_statuses_index' do + account.enqueue_update_public_statuses_index + expect(account).to have_received(:enqueue_remove_from_public_statuses_index) + expect(account).to_not have_received(:enqueue_add_to_public_statuses_index) + end + end + end + + describe '#enqueue_add_to_public_statuses_index' do + let(:indexable) { true } + let(:worker) { AddToPublicStatusesIndexWorker } + + before do + allow(worker).to receive(:perform_async) + end + + it 'enqueues AddToPublicStatusesIndexWorker' do + account.enqueue_add_to_public_statuses_index + expect(worker).to have_received(:perform_async).with(account.id) + end + end + + describe '#enqueue_remove_from_public_statuses_index' do + let(:indexable) { false } + let(:worker) { RemoveFromPublicStatusesIndexWorker } + + before do + allow(worker).to receive(:perform_async) + end + + it 'enqueues RemoveFromPublicStatusesIndexWorker' do + account.enqueue_remove_from_public_statuses_index + expect(worker).to have_received(:perform_async).with(account.id) + end + end +end diff --git a/spec/workers/add_to_public_statuses_index_worker_spec.rb b/spec/workers/add_to_public_statuses_index_worker_spec.rb new file mode 100644 index 0000000000..fa15072241 --- /dev/null +++ b/spec/workers/add_to_public_statuses_index_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AddToPublicStatusesIndexWorker do + describe '#perform' do + let(:account) { Fabricate(:account, indexable: indexable) } + let(:account_id) { account.id } + + before do + allow(Account).to receive(:find).with(account_id).and_return(account) unless account.nil? + allow(account).to receive(:add_to_public_statuses_index!) unless account.nil? + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'adds the account to the public statuses index' do + subject.perform(account_id) + expect(account).to have_received(:add_to_public_statuses_index!) + end + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'does not add the account to public statuses index' do + subject.perform(account_id) + expect(account).to_not have_received(:add_to_public_statuses_index!) + end + end + + context 'when account does not exist' do + let(:account) { nil } + let(:account_id) { 999 } + + it 'does not raise an error' do + expect { subject.perform(account_id) }.to_not raise_error + end + end + end +end diff --git a/spec/workers/remove_from_public_statuses_index_worker_spec.rb b/spec/workers/remove_from_public_statuses_index_worker_spec.rb new file mode 100644 index 0000000000..43ff211eaa --- /dev/null +++ b/spec/workers/remove_from_public_statuses_index_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RemoveFromPublicStatusesIndexWorker do + describe '#perform' do + let(:account) { Fabricate(:account, indexable: indexable) } + let(:account_id) { account.id } + + before do + allow(Account).to receive(:find).with(account_id).and_return(account) unless account.nil? + allow(account).to receive(:remove_from_public_statuses_index!) unless account.nil? + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'removes the account from public statuses index' do + subject.perform(account_id) + expect(account).to have_received(:remove_from_public_statuses_index!) + end + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'does not remove the account from public statuses index' do + subject.perform(account_id) + expect(account).to_not have_received(:remove_from_public_statuses_index!) + end + end + + context 'when account does not exist' do + let(:account) { nil } + let(:account_id) { 999 } + + it 'does not raise an error' do + expect { subject.perform(account_id) }.to_not raise_error + end + end + end +end From 4a7afb8e5920f1c181bb115a49a00991f12c2e67 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 24 Aug 2023 21:07:39 +0200 Subject: [PATCH 002/182] Fix changelog wording and missing items (#26638) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 107dfaca3f..fe66adc08a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,7 +101,7 @@ The following changelog entries focus on changes visible to users, administrator - **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26452)) - **Change account search to match by text when opted-in** ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25599), [Gargron](https://github.com/mastodon/mastodon/pull/26378)) - **Change import feature to be clearer, less error-prone and more reliable** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21054), [mgmn](https://github.com/mastodon/mastodon/pull/24874)) -- **Change local and federated timelines to be in a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247)) +- **Change local and federated timelines to be tabs of a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247)) - **Change user archive export to be faster and more reliable, and export `.zip` archives instead of `.tar.gz` ones** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23360), [TheEssem](https://github.com/mastodon/mastodon/pull/25034)) - **Change `mastodon-streaming` systemd unit files to be templated** ([e-nomem](https://github.com/mastodon/mastodon/pull/24751)) - **Change `statsd` integration to disable sidekiq metrics by default** ([mjankowski](https://github.com/mastodon/mastodon/pull/25265), [mjankowski](https://github.com/mastodon/mastodon/pull/25336), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26310)) @@ -189,6 +189,7 @@ The following changelog entries focus on changes visible to users, administrator - **Fix log-in flow when involving both OAuth and external authentication** ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24073)) - **Fix broken links in account gallery** ([c960657](https://github.com/mastodon/mastodon/pull/24218)) - **Fix blocking subdomains of an already-blocked domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26392)) +- **Fix migration handler not updating lists** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24808)) - Fix uploading of video files for which `ffprobe` reports `0/0` average framerate ([NicolaiSoeborg](https://github.com/mastodon/mastodon/pull/26500)) - Fix cached posts including stale stats ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26409)) - Fix adding column with default value taking longer on Postgres >= 11 ([Gargron](https://github.com/mastodon/mastodon/pull/26375)) From 3a75c0fdc3cb6d36c18cf260c3aa81c516ce3546 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:08:44 +0200 Subject: [PATCH 003/182] Update babel monorepo to v7.22.11 (#26640) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 129 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/yarn.lock b/yarn.lock index 216e22f681..6894f4388c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,24 +50,24 @@ integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.10.4", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.11.tgz#8033acaa2aa24c3f814edaaa057f3ce0ba559c24" + integrity sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.10" "@babel/generator" "^7.22.10" "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" + "@babel/helpers" "^7.22.11" + "@babel/parser" "^7.22.11" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.11" + "@babel/types" "^7.22.11" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" + json5 "^2.2.3" semver "^6.3.1" "@babel/generator@^7.22.10", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": @@ -113,6 +113,21 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz#4078686740459eeb4af3494a273ac09148dfb213" + integrity sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" @@ -268,14 +283,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.10" -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== +"@babel/helpers@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.11.tgz#b02f5d5f2d7abc21ab59eeed80de410ba70b056a" + integrity sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.11" + "@babel/types" "^7.22.11" "@babel/highlight@^7.22.10", "@babel/highlight@^7.22.5": version "7.22.10" @@ -286,11 +301,16 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.22.11", "@babel/parser@^7.22.5": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" + integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -640,6 +660,15 @@ "@babel/helper-module-transforms" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-modules-commonjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz#d7991d3abad199c03b68ee66a64f216c47ffdfae" + integrity sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g== + dependencies: + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-commonjs@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" @@ -683,9 +712,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-nullish-coalescing-operator@^7.22.3", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" - integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" + integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -877,13 +906,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" - integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== +"@babel/plugin-transform-typescript@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz#9f27fb5e51585729374bb767ab6a6d9005a23329" + integrity sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -1026,15 +1055,15 @@ "@babel/plugin-transform-react-pure-annotations" "^7.22.5" "@babel/preset-typescript@^7.21.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" - integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz#f218cd0345524ac888aa3dc32f029de5b064b575" + integrity sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.5" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-typescript" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.11" + "@babel/plugin-transform-typescript" "^7.22.11" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1049,9 +1078,9 @@ 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.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": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" - integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" + integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA== dependencies: regenerator-runtime "^0.14.0" @@ -1080,10 +1109,10 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" - integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== +"@babel/traverse@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.11.tgz#71ebb3af7a05ff97280b83f05f8865ac94b2027c" + integrity sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ== dependencies: "@babel/code-frame" "^7.22.10" "@babel/generator" "^7.22.10" @@ -1091,12 +1120,12 @@ "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/parser" "^7.22.11" + "@babel/types" "^7.22.11" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== @@ -1114,6 +1143,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2" + integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3901,7 +3939,12 @@ caniuse-lite@^1.0.30001502: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz#418aefeed9d024cd3129bfae0ccc782d4cb8f12b" integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== -caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520: +caniuse-lite@^1.0.30001517: + version "1.0.30001522" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" + integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== + +caniuse-lite@^1.0.30001520: version "1.0.30001520" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz#62e2b7a1c7b35269594cf296a80bdf8cb9565006" integrity sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA== @@ -5050,9 +5093,9 @@ electron-to-chromium@^1.4.428: integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== electron-to-chromium@^1.4.477: - version "1.4.490" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz#d99286f6e915667fa18ea4554def1aa60eb4d5f1" - integrity sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A== + version "1.4.500" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz#7dd05fdfbe02ed34b9f6099cfe01407b473d5af7" + integrity sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A== elliptic@^6.5.3: version "6.5.4" @@ -7844,7 +7887,7 @@ json5@^1.0.1, json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.0, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== From d8276ad1b43681a90825a8c1f9d4bcc8b8f302c0 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 11:31:20 +0200 Subject: [PATCH 004/182] Add `data-nosnippet` attribute to remote posts and local posts with `noindex` (#26648) --- app/javascript/mastodon/components/status.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 30692d1cd9..e1728910ee 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -550,7 +550,7 @@ class Status extends ImmutablePureComponent { return ( -
+
{prepend}
From a9a06f5ffecc402b050bef3a3ea51e43d99ba7c3 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 12:01:55 +0200 Subject: [PATCH 005/182] Add PublicStatusesCheck to Elasticsearch index check on admin dashboard (#26650) --- app/lib/admin/system_check/elasticsearch_check.rb | 1 + spec/lib/admin/system_check/elasticsearch_check_spec.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index 91070756cb..2092ef0dd0 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -6,6 +6,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck AccountsIndex, TagsIndex, StatusesIndex, + PublicStatusesIndex, ].freeze def skip? diff --git a/spec/lib/admin/system_check/elasticsearch_check_spec.rb b/spec/lib/admin/system_check/elasticsearch_check_spec.rb index f3918d403d..a885640ce0 100644 --- a/spec/lib/admin/system_check/elasticsearch_check_spec.rb +++ b/spec/lib/admin/system_check/elasticsearch_check_spec.rb @@ -17,6 +17,7 @@ describe Admin::SystemCheck::ElasticsearchCheck do allow(Chewy.client.indices).to receive_messages(get_mapping: { AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys, StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys, + PublicStatusesIndex.index_name => PublicStatusesIndex.mappings_hash.deep_stringify_keys, InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys, TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys, }, get_settings: { @@ -90,6 +91,7 @@ describe Admin::SystemCheck::ElasticsearchCheck do allow(Chewy.client.indices).to receive(:get_mapping).and_return({ AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys, StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys, + PublicStatusesIndex.index_name => PublicStatusesIndex.mappings_hash.deep_stringify_keys, InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys, TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys, }) From 11b120cb643e21fc7df88bd79cfb1dfbcbccf691 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 12:02:09 +0200 Subject: [PATCH 006/182] =?UTF-8?q?Add=20Elasticsearch/OpenSearch=20versio?= =?UTF-8?q?n=20to=20=E2=80=9CSoftware=E2=80=9D=20in=20admin=20dashboard=20?= =?UTF-8?q?(#26652)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dimension/software_versions_dimension.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb index 9ab3776c91..72a98a88ab 100644 --- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb +++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb @@ -10,7 +10,7 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim protected def perform_query - [mastodon_version, ruby_version, postgresql_version, redis_version] + [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version].compact end def mastodon_version @@ -57,6 +57,22 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim } end + def elasticsearch_version + return unless Chewy.enabled? + + client_info = Chewy.client.info + version = client_info.dig('version', 'number') + + { + key: 'elasticsearch', + human_key: client_info.dig('version', 'distribution') == 'opensearch' ? 'OpenSearch' : 'Elasticsearch', + value: version, + human_value: version, + } + rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error + nil + end + def redis_info @redis_info ||= if redis.is_a?(Redis::Namespace) redis.redis.info From 2a5c1dca2e63835cd7c033464c50a8a299593cd9 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 17:01:56 +0200 Subject: [PATCH 007/182] Fix statuses search Elasticsearch query (#26657) --- app/services/statuses_search_service.rb | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 21d6b71b7d..2a4e533cfb 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -15,7 +15,7 @@ class StatusesSearchService < BaseService def status_search_results definition = parsed_query.apply( - StatusesIndex.filter( + Chewy::Search::Request.new(StatusesIndex, PublicStatusesIndex).filter( bool: { should: [ publicly_searchable, @@ -27,9 +27,6 @@ class StatusesSearchService < BaseService ) ) - # This is the best way to submit identical queries to multi-indexes though chewy - definition.instance_variable_get(:@parameters)[:indices].value[:indices] << PublicStatusesIndex - results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact account_ids = results.map(&:account_id) account_domains = results.map(&:account_domain) @@ -42,13 +39,7 @@ class StatusesSearchService < BaseService def publicly_searchable { - bool: { - must_not: { - exists: { - field: 'searchable_by', - }, - }, - }, + term: { _index: PublicStatusesIndex.index_name }, } end @@ -57,9 +48,7 @@ class StatusesSearchService < BaseService bool: { must: [ { - exists: { - field: 'searchable_by', - }, + term: { _index: StatusesIndex.index_name }, }, { term: { searchable_by: @account.id }, From c0d98abf00a9b878353c4d1b6b31383244992dec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 17:02:33 +0200 Subject: [PATCH 008/182] Update dependency immutable to v4.3.4 (#26655) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6894f4388c..a630a752e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6686,9 +6686,9 @@ immutable@^3.8.2: integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg== immutable@^4.0.0, immutable@^4.0.0-rc.1, immutable@^4.3.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.3.tgz#8934ff6826d996a7642c8dc4b46e694dd19561e3" - integrity sha512-808ZFYMsIRAjLAu5xkKo0TsbY9LBy9H5MazTKIEHerNkg0ymgilGfBPMR/3G7d/ihGmuK2Hw8S1izY2d3kd3wA== + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== import-fresh@^3.2.1: version "3.3.0" From 5a74675fce9635fc5bbf8a142c29de872285518d Mon Sep 17 00:00:00 2001 From: jsgoldstein Date: Fri, 25 Aug 2023 12:16:08 -0400 Subject: [PATCH 009/182] Update ordering to use `id` from body of document instead of deprecated `_id` (#26659) --- app/services/statuses_search_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 2a4e533cfb..0d0de2a9d2 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -27,7 +27,7 @@ class StatusesSearchService < BaseService ) ) - results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact + results = definition.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact account_ids = results.map(&:account_id) account_domains = results.map(&:account_domain) preloaded_relations = @account.relations_map(account_ids, account_domains) From c833282412b5dec6999fa76ac74cd8ba8a0394c5 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 18:17:22 +0200 Subject: [PATCH 010/182] Fix dashboard check for Elasticsearch suggested command including incorrect names (#26658) --- app/lib/admin/system_check/elasticsearch_check.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index 2092ef0dd0..c0a1a21e86 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -86,7 +86,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck def mismatched_indexes @mismatched_indexes ||= INDEXES.filter_map do |klass| - klass.index_name if Chewy.client.indices.get_mapping[klass.index_name]&.deep_symbolize_keys != klass.mappings_hash + klass.base_name if Chewy.client.indices.get_mapping[klass.index_name]&.deep_symbolize_keys != klass.mappings_hash end end From 1201da375273016625fd6b680ee3c693d0e9e82a Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 18:26:44 +0200 Subject: [PATCH 011/182] Change the pre-release versioning scheme and associated environment variables (#26653) --- .github/workflows/build-container-image.yml | 6 ++++-- .github/workflows/build-nightly.yml | 9 ++++----- .github/workflows/build-push-pr.yml | 6 +++--- Dockerfile | 8 ++++---- lib/mastodon/version.rb | 17 ++++++++++++----- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 1b15d19885..8e9c747664 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -8,7 +8,9 @@ on: type: boolean push_to_images: type: string - version_suffix: + version_prerelease: + type: string + version_metadata: type: string flavor: type: string @@ -83,7 +85,7 @@ jobs: - uses: docker/build-push-action@v4 with: context: . - build-args: MASTODON_VERSION_SUFFIX=${{ inputs.version_suffix }} + build-args: MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} platforms: ${{ inputs.platforms }} provenance: false builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index 5b34c1c3c6..5feb2ade33 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -16,9 +16,9 @@ jobs: env: TZ: Etc/UTC run: | - echo mastodon_version_suffix=nightly.$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT + echo mastodon_version_prerelease=nightly.$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT outputs: - suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }} + prerelease: ${{ steps.version_vars.outputs.mastodon_version_prerelease }} build-image: needs: compute-suffix @@ -29,8 +29,7 @@ jobs: push_to_images: | tootsuite/mastodon ghcr.io/mastodon/mastodon - # The `-` is important here, result will be v4.1.2-nightly.2022-03-05 - version_suffix: -${{ needs.compute-suffix.outputs.suffix }} + version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }} labels: | org.opencontainers.image.description=Nightly build image used for testing purposes flavor: | @@ -38,5 +37,5 @@ jobs: tags: | type=raw,value=edge type=raw,value=nightly - type=schedule,pattern=${{ needs.compute-suffix.outputs.suffix }} + type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} secrets: inherit diff --git a/.github/workflows/build-push-pr.yml b/.github/workflows/build-push-pr.yml index 2d20261286..b95e3c14e0 100644 --- a/.github/workflows/build-push-pr.yml +++ b/.github/workflows/build-push-pr.yml @@ -21,9 +21,9 @@ jobs: uses: actions/checkout@v3 - id: version_vars run: | - echo mastodon_version_suffix=+pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT + echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT outputs: - suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }} + metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }} build-image: needs: compute-suffix @@ -33,7 +33,7 @@ jobs: use_native_arm64_builder: true push_to_images: | ghcr.io/mastodon/mastodon - version_suffix: ${{ needs.compute-suffix.outputs.suffix }} + version_metadata: ${{ needs.compute-suffix.outputs.metadata }} flavor: | latest=auto tags: | diff --git a/Dockerfile b/Dockerfile index cb5096581c..cdabc4c7c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,8 +42,8 @@ RUN apt-get update && \ FROM node:${NODE_VERSION} # Use those args to specify your own version flags & suffixes -ARG MASTODON_VERSION_FLAGS="" -ARG MASTODON_VERSION_SUFFIX="" +ARG MASTODON_VERSION_PRERELEASE="" +ARG MASTODON_VERSION_METADATA="" ARG UID="991" ARG GID="991" @@ -89,8 +89,8 @@ ENV RAILS_ENV="production" \ NODE_ENV="production" \ RAILS_SERVE_STATIC_FILES="true" \ BIND="0.0.0.0" \ - MASTODON_VERSION_FLAGS="${MASTODON_VERSION_FLAGS}" \ - MASTODON_VERSION_SUFFIX="${MASTODON_VERSION_SUFFIX}" + MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ + MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" # Set the run user USER mastodon diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index e3347ca17c..c542d5d49a 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -16,12 +16,16 @@ module Mastodon 0 end - def flags - ENV['MASTODON_VERSION_FLAGS'].presence || '-beta2' + def default_prerelease + 'beta2' end - def suffix - ENV.fetch('MASTODON_VERSION_SUFFIX', '') + def prerelease + ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease + end + + def build_metadata + ENV.fetch('MASTODON_VERSION_METADATA', nil) end def to_a @@ -29,7 +33,10 @@ module Mastodon end def to_s - [to_a.join('.'), flags, suffix].join + components = [to_a.join('.')] + components << "-#{prerelease}" if prerelease.present? + components << "+#{build_metadata}" if build_metadata.present? + components.join end def repository From 8784abbc4c58b2ad64ada47dd0896bf699753c0c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 25 Aug 2023 22:03:04 +0200 Subject: [PATCH 012/182] Fix toast saying "published" instead of "saved" after editing post in web UI (#26664) --- app/javascript/mastodon/actions/compose.js | 3 ++- app/javascript/mastodon/locales/en.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 260fb43f08..6abfd6157e 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -84,6 +84,7 @@ const messages = defineMessages({ uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, open: { id: 'compose.published.open', defaultMessage: 'Open' }, published: { id: 'compose.published.body', defaultMessage: 'Post published.' }, + saved: { id: 'compose.saved.body', defaultMessage: 'Post saved.' }, }); export const ensureComposeIsVisible = (getState, routerHistory) => { @@ -244,7 +245,7 @@ export function submitCompose(routerHistory) { } dispatch(showAlert({ - message: messages.published, + message: statusId === null ? messages.published : messages.saved, action: messages.open, dismissAfter: 10000, onClick: () => routerHistory.push(`/@${response.data.account.username}/${response.data.id}`), diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d4cd8c7945..5eeaf8044b 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -137,6 +137,7 @@ "compose.language.search": "Search languages...", "compose.published.body": "Post published.", "compose.published.open": "Open", + "compose.saved.body": "Post saved.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any sensitive information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.", From 2cb30c25dc534f5f5522b4f64864559a0af9fb1c Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 26 Aug 2023 17:34:36 +0200 Subject: [PATCH 013/182] Fix nightly build version (#26676) --- .github/workflows/build-container-image.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 8e9c747664..897bb9caaa 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -85,7 +85,9 @@ jobs: - uses: docker/build-push-action@v4 with: context: . - build-args: MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} + build-args: | + MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} + MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} platforms: ${{ inputs.platforms }} provenance: false builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} From a0373d049d237297d27050f82adacc670dba3b07 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 18:58:20 +0200 Subject: [PATCH 014/182] Change queue of job when opting into search from `default` to `pull` (#26688) --- app/workers/add_to_public_statuses_index_worker.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb index 409e5e7086..33529a4c1d 100644 --- a/app/workers/add_to_public_statuses_index_worker.rb +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -3,6 +3,8 @@ class AddToPublicStatusesIndexWorker include Sidekiq::Worker + sidekiq_options queue: 'pull' + def perform(account_id) account = Account.find(account_id) From 4456f65da4da07b37132130d9049c6684d379085 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 22:37:35 +0200 Subject: [PATCH 015/182] Fix unnecessary condition causing seqscan when indexing (#26689) --- app/models/concerns/account_statuses_search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb index 563a871051..626bf38900 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account_statuses_search.rb @@ -31,7 +31,7 @@ module AccountStatusesSearch def add_to_public_statuses_index! return unless Chewy.enabled? - statuses.indexable.find_in_batches do |batch| + statuses.without_reblogs.where(visibility: :public).find_in_batches do |batch| PublicStatusesIndex.import(query: batch) end end From f071f72a3c843f50231ee3bb02b04c1ae343108a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 22:38:01 +0200 Subject: [PATCH 016/182] Change indexing jobs to use database replica (#26692) --- app/workers/add_to_public_statuses_index_worker.rb | 11 ++++++++--- app/workers/scheduler/indexing_scheduler.rb | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb index 33529a4c1d..80d921eab0 100644 --- a/app/workers/add_to_public_statuses_index_worker.rb +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -2,15 +2,20 @@ class AddToPublicStatusesIndexWorker include Sidekiq::Worker + include DatabaseHelper sidekiq_options queue: 'pull' def perform(account_id) - account = Account.find(account_id) + with_primary do + @account = Account.find(account_id) + end - return unless account.indexable? + return unless @account.indexable? - account.add_to_public_statuses_index! + with_read_replica do + @account.add_to_public_statuses_index! + end rescue ActiveRecord::RecordNotFound true end diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 6c770d5a8f..1b09730c7d 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -3,6 +3,7 @@ class Scheduler::IndexingScheduler include Sidekiq::Worker include Redisable + include DatabaseHelper sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.day.to_i @@ -15,7 +16,10 @@ class Scheduler::IndexingScheduler indexes.each do |type| with_redis do |redis| redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE).each_slice(IMPORT_BATCH_SIZE) do |ids| - type.import!(ids) + with_read_replica do + type.import!(ids) + end + redis.srem("chewy:queue:#{type.name}", ids) end end From e886929716ec7d344c580c99174cce1390874533 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:20:02 +0200 Subject: [PATCH 017/182] Update eslint (non-major) (#26694) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 142 +++++++++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/yarn.lock b/yarn.lock index a630a752e2..8804218290 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,9 +1287,9 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" - integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== + version "4.8.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.0.tgz#11195513186f68d42fbf449f9a7136b2c0c92005" + integrity sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg== "@eslint/eslintrc@^2.1.2": version "2.1.2" @@ -1306,10 +1306,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" - integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== +"@eslint/js@8.48.0": + version "8.48.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb" + integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw== "@floating-ui/core@^1.3.1": version "1.3.1" @@ -2608,15 +2608,15 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^6.0.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz#53428b616f7d80fe879f45a08f11cc0f0b62cf13" - integrity sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg== + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz#bc0c6f000134b53c304ad0bec4ee4753cd3e89d2" + integrity sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/type-utils" "6.4.0" - "@typescript-eslint/utils" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/type-utils" "6.4.1" + "@typescript-eslint/utils" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -2625,31 +2625,31 @@ ts-api-utils "^1.0.1" "@typescript-eslint/parser@^6.0.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.0.tgz#47e7c6e22ff1248e8675d95f488890484de67600" - integrity sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg== + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.1.tgz#85ad550bf4ac4aa227504f1becb828f8e46c44e3" + integrity sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg== dependencies: - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/typescript-estree" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/typescript-estree" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz#3048e4262ba3eafa4e2e69b08912d9037ec646ae" - integrity sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig== +"@typescript-eslint/scope-manager@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz#4b073a30be2dbe603e44e9ae0cff7e1d3ed19278" + integrity sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A== dependencies: - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" -"@typescript-eslint/type-utils@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz#c8ac92716ed6a9d5443aa3e342910355b0796ba0" - integrity sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg== +"@typescript-eslint/type-utils@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz#fa21cb13016c8d6f352fe9b2d6c9ab6edc2d1857" + integrity sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA== dependencies: - "@typescript-eslint/typescript-estree" "6.4.0" - "@typescript-eslint/utils" "6.4.0" + "@typescript-eslint/typescript-estree" "6.4.1" + "@typescript-eslint/utils" "6.4.1" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -2658,10 +2658,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== -"@typescript-eslint/types@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.0.tgz#5b109a59a805f0d8d375895e42d9e5f0037f66ee" - integrity sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg== +"@typescript-eslint/types@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.1.tgz#b2c61159f46dda210fed9f117f5d027f65bb5c3b" + integrity sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg== "@typescript-eslint/typescript-estree@5.59.0": version "5.59.0" @@ -2676,30 +2676,30 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz#3c58d20632db93fec3d6ab902acbedf593d37276" - integrity sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA== +"@typescript-eslint/typescript-estree@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz#91ff88101c710adb0f70a317f2f65efa9441da45" + integrity sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg== dependencies: - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.4.0.tgz#23e996b693603c5924b1fbb733cc73196256baa5" - integrity sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw== +"@typescript-eslint/utils@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.4.1.tgz#81bf62ff0c3119a26c19fab683582e29450717bc" + integrity sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/typescript-estree" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/typescript-estree" "6.4.1" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.59.0": @@ -2710,12 +2710,12 @@ "@typescript-eslint/types" "5.59.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz#96a426cdb1add28274abd7a34aefe27f8b7d51ef" - integrity sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA== +"@typescript-eslint/visitor-keys@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz#e3ccf7b8d42e625946ac5094ed92a405fb4115e0" + integrity sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ== dependencies: - "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/types" "6.4.1" eslint-visitor-keys "^3.4.1" "@webassemblyjs/ast@1.9.0": @@ -5528,14 +5528,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.41.0: - version "8.47.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806" - integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q== + version "8.48.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.48.0.tgz#bf9998ba520063907ba7bfe4c480dc8be03c2155" + integrity sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "^8.47.0" + "@eslint/js" "8.48.0" "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5990,14 +5990,15 @@ findup-sync@^3.0.0: resolve-dir "^1.0.1" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== dependencies: - flatted "^3.1.0" + flatted "^3.2.7" + keyv "^4.5.3" rimraf "^3.0.2" -flatted@^3.1.0: +flatted@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== @@ -7843,6 +7844,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -7934,6 +7940,13 @@ keycode@^2.1.7: resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.1.tgz#09c23b2be0611d26117ea2501c2c391a01f39eff" integrity sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg== +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -11329,7 +11342,6 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From e50d54b91a969f29710833c325501d9a20e0625b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 10:31:51 +0200 Subject: [PATCH 018/182] Fix not being able to negate prefix clauses in search (#26672) --- app/lib/search_query_parser.rb | 2 +- app/lib/search_query_transformer.rb | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 15956d4cfd..5d6ffbf29d 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -9,7 +9,7 @@ class SearchQueryParser < Parslet::Parser rule(:prefix) { (term >> colon).as(:prefix) } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } - rule(:clause) { (prefix.maybe >> operator.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:clause) { (operator.maybe >> prefix.maybe >> (phrase | term | shortcode)).as(:clause) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index dad99cbd2d..915f9c3312 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -36,7 +36,11 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_filter(clause) case clause when PrefixClause - { clause.type => { clause.filter => clause.term } } + if clause.negated? + { bool: { must_not: { clause.type => { clause.filter => clause.term } } } } + else + { clause.type => { clause.filter => clause.term } } + end else raise "Unexpected clause type: #{clause}" end @@ -81,7 +85,8 @@ class SearchQueryTransformer < Parslet::Transform class PrefixClause attr_reader :type, :filter, :operator, :term - def initialize(prefix, term) + def initialize(prefix, operator, term) + @negated = operator == '-' @operator = :filter case prefix @@ -114,6 +119,10 @@ class SearchQueryTransformer < Parslet::Transform end end + def negated? + @negated + end + private def account_id_from_term(term) @@ -132,7 +141,7 @@ class SearchQueryTransformer < Parslet::Transform operator = clause[:operator]&.to_s if clause[:prefix] - PrefixClause.new(prefix, clause[:term].to_s) + PrefixClause.new(prefix, operator, clause[:term].to_s) elsif clause[:term] TermClause.new(prefix, operator, clause[:term].to_s) elsif clause[:shortcode] From 62bc0e37d280c3959240bf6dee3bf9d9e57ae308 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:32:13 +0200 Subject: [PATCH 019/182] Update dependency haml_lint to v0.50.0 (#26665) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f801b1b560..7a68249c28 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -324,7 +324,7 @@ GEM ruby-progressbar (~> 1.4) globalid (1.1.0) activesupport (>= 5.0) - haml (6.1.1) + haml (6.1.2) temple (>= 0.8.2) thor tilt @@ -333,7 +333,7 @@ GEM activesupport (>= 5.1) haml (>= 4.0.6) railties (>= 5.1) - haml_lint (0.49.3) + haml_lint (0.50.0) haml (>= 4.0, < 6.2) parallel (~> 1.10) rainbow From c51564cc677e1bae7f80de0394f55653648e3de6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:04:06 +0200 Subject: [PATCH 020/182] Update DefinitelyTyped types (non-major) (#26693) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 135 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8804218290..4a6d642cef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1538,12 +1538,12 @@ "@types/node" "*" jest-mock "^29.6.2" -"@jest/expect-utils@^29.6.2": - version "29.6.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" - integrity sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg== +"@jest/expect-utils@^29.6.2", "@jest/expect-utils@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.4.tgz#17c7dfe6cec106441f218b0aff4b295f98346679" + integrity sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg== dependencies: - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" "@jest/expect@^29.6.2": version "29.6.2" @@ -1605,10 +1605,10 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.6.0": - version "29.6.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" - integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" @@ -1662,12 +1662,12 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.6.1": - version "29.6.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" - integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== +"@jest/types@^29.6.1", "@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.6.0" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2226,9 +2226,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@*", "@types/jest@^29.5.2": - version "29.5.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" - integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== + version "29.5.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2288,9 +2288,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.4.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.9.tgz#c7164e0f8d3f12dfae336af0b1f7fdec8c6b204f" - integrity sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ== + version "20.5.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377" + integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA== "@types/node@14 || 16 || 17": version "17.0.45" @@ -2466,9 +2466,9 @@ "@types/react" "*" "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7": - version "18.2.20" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.20.tgz#1605557a83df5c8a2cc4eeb743b3dfc0eb6aaeb2" - integrity sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw== + version "18.2.21" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" + integrity sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -4922,10 +4922,10 @@ detect-passive-events@^2.0.3: dependencies: detect-it "^4.0.1" -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diffie-hellman@^5.0.0: version "5.0.3" @@ -5734,7 +5734,18 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^29.0.0, expect@^29.6.2: +expect@^29.0.0: + version "29.6.4" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.4.tgz#a6e6f66d4613717859b2fe3da98a739437b6f4b8" + integrity sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA== + dependencies: + "@jest/expect-utils" "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + +expect@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" integrity sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA== @@ -7431,15 +7442,15 @@ jest-config@^29.6.2: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" - integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== +jest-diff@^29.6.2, jest-diff@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" + integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== dependencies: chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" jest-docblock@^29.4.3: version "29.4.3" @@ -7485,10 +7496,10 @@ jest-environment-node@^29.6.2: jest-mock "^29.6.2" jest-util "^29.6.2" -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.4.3, jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^29.6.2: version "29.6.2" @@ -7517,28 +7528,28 @@ jest-leak-detector@^29.6.2: jest-get-type "^29.4.3" pretty-format "^29.6.2" -jest-matcher-utils@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" - integrity sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ== +jest-matcher-utils@^29.6.2, jest-matcher-utils@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz#327db7ababea49455df3b23e5d6109fe0c709d24" + integrity sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ== dependencies: chalk "^4.0.0" - jest-diff "^29.6.2" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" -jest-message-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" - integrity sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ== +jest-message-util@^29.6.2, jest-message-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.3.tgz#bce16050d86801b165f20cfde34dc01d3cf85fbf" + integrity sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.1" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.6.2" + pretty-format "^29.6.3" slash "^3.0.0" stack-utils "^2.0.3" @@ -7665,12 +7676,12 @@ jest-snapshot@^29.6.2: pretty-format "^29.6.2" semver "^7.5.3" -jest-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" - integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== +jest-util@^29.6.2, jest-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.3.tgz#e15c3eac8716440d1ed076f09bc63ace1aebca63" + integrity sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA== dependencies: - "@jest/types" "^29.6.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -9710,12 +9721,12 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" - integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== +pretty-format@^29.0.0, pretty-format@^29.6.2, pretty-format@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" + integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== dependencies: - "@jest/schemas" "^29.6.0" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" From 4b24a994c1e2bc2322941e159183e09c94614b9c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 11:36:17 +0200 Subject: [PATCH 021/182] Fix multiple issues with status index mappings (#26686) --- app/chewy/accounts_index.rb | 9 +++++---- app/chewy/public_statuses_index.rb | 14 ++++++++++---- app/chewy/statuses_index.rb | 14 ++++++++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 61e3399aa8..8881b08f66 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -21,19 +21,20 @@ class AccountsIndex < Chewy::Index analyzer: { natural: { - tokenizer: 'uax_url_email', + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), }, verbatim: { - tokenizer: 'standard', + tokenizer: 'uax_url_email', filter: %w(lowercase asciifolding cjk_width), }, @@ -62,6 +63,6 @@ class AccountsIndex < Chewy::Index field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } - field(:text, type: 'text', analyzer: 'whitespace', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } end end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 1fad5de3a1..5c68a13658 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -20,13 +20,19 @@ class PublicStatusesIndex < Chewy::Index }, analyzer: { - content: { + verbatim: { tokenizer: 'uax_url_email', + filter: %w(lowercase), + }, + + content: { + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), @@ -40,9 +46,9 @@ class PublicStatusesIndex < Chewy::Index .includes(:media_attachments, :preloadable_poll, :preview_cards) root date_detection: false do - field(:id, type: 'keyword') + field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:created_at, type: 'date') diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 130f8801df..6d33521051 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -20,13 +20,19 @@ class StatusesIndex < Chewy::Index }, analyzer: { - content: { + verbatim: { tokenizer: 'uax_url_email', + filter: %w(lowercase), + }, + + content: { + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), @@ -64,9 +70,9 @@ class StatusesIndex < Chewy::Index end root date_detection: false do - field(:id, type: 'keyword') + field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) From 85c3155d14a6261899355c722dd12158c013ed8e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:19:42 +0200 Subject: [PATCH 022/182] Update dependency @testing-library/jest-dom to v6 (#26479) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Renaud Chaput --- app/javascript/mastodon/test_setup.js | 2 +- package.json | 2 +- yarn.lock | 25 +++++++++---------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/javascript/mastodon/test_setup.js b/app/javascript/mastodon/test_setup.js index 666127af39..7b0828bfa8 100644 --- a/app/javascript/mastodon/test_setup.js +++ b/app/javascript/mastodon/test_setup.js @@ -1 +1 @@ -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; diff --git a/package.json b/package.json index 16c5d529a6..4038831050 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ }, "devDependencies": { "@formatjs/cli": "^6.1.1", - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@types/babel__core": "^7.20.1", "@types/emoji-mart": "^3.0.9", diff --git a/yarn.lock b/yarn.lock index 4a6d642cef..3e9c9abf77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2022,14 +2022,13 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.16.5": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" - integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg== +"@testing-library/jest-dom@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.0.0.tgz#d2ba5a3fd13724d5966b3f8cd24d2cedcab4fa76" + integrity sha512-Ye2R3+/oM27jir8CzYPmuWdavTaKwNZcu0d22L9pO/vnOYE0wmrtpw79TQJa8H6gV8/i7yd+pLaqeLlA0rTMfg== dependencies: "@adobe/css-tools" "^4.0.1" "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" @@ -2225,10 +2224,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^29.5.2": - version "29.5.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" - integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== +"@types/jest@^29.5.2": + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2535,13 +2534,6 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" - integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== - dependencies: - "@types/jest" "*" - "@types/tough-cookie@*": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" @@ -11353,6 +11345,7 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 4ecbd5bb401595e1d41d36153957022c1ec9d422 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 12:43:00 +0200 Subject: [PATCH 023/182] Add `from:me` syntax to search (#26660) --- app/lib/search_query_transformer.rb | 13 ++++++++----- app/services/statuses_search_service.rb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 915f9c3312..f1fc687e47 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -85,8 +85,9 @@ class SearchQueryTransformer < Parslet::Transform class PrefixClause attr_reader :type, :filter, :operator, :term - def initialize(prefix, operator, term) + def initialize(prefix, operator, term, options = {}) @negated = operator == '-' + @options = options @operator = :filter case prefix @@ -105,15 +106,15 @@ class SearchQueryTransformer < Parslet::Transform when 'before' @filter = :created_at @type = :range - @term = { lt: term } + @term = { lt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } when 'after' @filter = :created_at @type = :range - @term = { gt: term } + @term = { gt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } when 'during' @filter = :created_at @type = :range - @term = { gte: term, lte: term } + @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } else raise Mastodon::SyntaxError end @@ -126,6 +127,8 @@ class SearchQueryTransformer < Parslet::Transform private def account_id_from_term(term) + return @options[:current_account]&.id || -1 if term == 'me' + username, domain = term.gsub(/\A@/, '').split('@') domain = nil if TagManager.instance.local_domain?(domain) account = Account.find_remote(username, domain) @@ -141,7 +144,7 @@ class SearchQueryTransformer < Parslet::Transform operator = clause[:operator]&.to_s if clause[:prefix] - PrefixClause.new(prefix, operator, clause[:term].to_s) + PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) elsif clause[:term] TermClause.new(prefix, operator, clause[:term].to_s) elsif clause[:shortcode] diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 0d0de2a9d2..2317a2a1ac 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -59,6 +59,6 @@ class StatusesSearchService < BaseService end def parsed_query - SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) + SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account) end end From 9ab2c6b4792f7f3ed07a460a3fdf391062127268 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 13:18:39 +0200 Subject: [PATCH 024/182] Add search options to search popout in web UI (#26662) --- .../features/compose/components/search.jsx | 47 +++++++++++++++++-- app/javascript/mastodon/locales/en.json | 4 ++ .../styles/mastodon/components.scss | 6 +++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx index 682f8d3c8c..1c629bcbb4 100644 --- a/app/javascript/mastodon/features/compose/components/search.jsx +++ b/app/javascript/mastodon/features/compose/components/search.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl'; import classNames from 'classnames'; @@ -45,6 +45,16 @@ class Search extends PureComponent { options: [], }; + defaultOptions = [ + { label: <>has: , action: e => { e.preventDefault(); this._insertText('has:') } }, + { label: <>is: , action: e => { e.preventDefault(); this._insertText('is:') } }, + { label: <>language: , action: e => { e.preventDefault(); this._insertText('language:') } }, + { label: <>from: , action: e => { e.preventDefault(); this._insertText('from:') } }, + { label: <>before: , action: e => { e.preventDefault(); this._insertText('before:') } }, + { label: <>during: , action: e => { e.preventDefault(); this._insertText('during:') } }, + { label: <>after: , action: e => { e.preventDefault(); this._insertText('after:') } }, + ]; + setRef = c => { this.searchForm = c; }; @@ -70,7 +80,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions(); + const options = this._getOptions().concat(this.defaultOptions); switch(e.key) { case 'Escape': @@ -100,11 +110,9 @@ class Search extends PureComponent { if (selectedOption === -1) { this._submit(); } else if (options.length > 0) { - options[selectedOption].action(); + options[selectedOption].action(e); } - this._unfocus(); - break; case 'Delete': if (selectedOption > -1 && options.length > 0) { @@ -147,6 +155,7 @@ class Search extends PureComponent { router.history.push(`/tags/${query}`); onClickSearchResult(query, 'hashtag'); + this._unfocus(); }; handleAccountClick = () => { @@ -157,6 +166,7 @@ class Search extends PureComponent { router.history.push(`/@${query}`); onClickSearchResult(query, 'account'); + this._unfocus(); }; handleURLClick = () => { @@ -164,6 +174,7 @@ class Search extends PureComponent { const { value, onOpenURL } = this.props; onOpenURL(value, router.history); + this._unfocus(); }; handleStatusSearch = () => { @@ -182,6 +193,8 @@ class Search extends PureComponent { } else if (search.get('type') === 'hashtag') { router.history.push(`/tags/${search.get('q')}`); } + + this._unfocus(); }; handleForgetRecentSearchClick = search => { @@ -194,6 +207,18 @@ class Search extends PureComponent { document.querySelector('.ui').parentElement.focus(); } + _insertText (text) { + const { value, onChange } = this.props; + + if (value === '') { + onChange(text); + } else if (value[value.length - 1] === ' ') { + onChange(`${value}${text}`); + } else { + onChange(`${value} ${text}`); + } + } + _submit (type) { const { onSubmit, openInRoute } = this.props; const { router } = this.context; @@ -203,6 +228,8 @@ class Search extends PureComponent { if (openInRoute) { router.history.push('/search'); } + + this._unfocus(); } _getOptions () { @@ -325,6 +352,16 @@ class Search extends PureComponent {
)} + +

+ +
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5eeaf8044b..5871b08def 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -586,8 +586,12 @@ "search.quick_action.open_url": "Open URL in Mastodon", "search.quick_action.status_search": "Posts matching {x}", "search.search_or_paste": "Search or paste URL", + "search_popout.language_code": "ISO language code", + "search_popout.options": "Search options", "search_popout.quick_actions": "Quick actions", "search_popout.recent": "Recent searches", + "search_popout.specific_date": "specific date", + "search_popout.user": "user", "search_results.accounts": "Profiles", "search_results.all": "All", "search_results.hashtags": "Hashtags", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 7e7bf4488c..6fa5c545ea 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4991,6 +4991,12 @@ a.status-card { } &__menu { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + &__message { color: $dark-text-color; padding: 0 10px; From c46d2977409a0723f9b8d5addb1f88e394cb968e Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 28 Aug 2023 15:04:57 +0200 Subject: [PATCH 025/182] Fix incorrect call to `PublicStatusesIndex.import` (#26697) --- app/models/concerns/account_statuses_search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb index 626bf38900..fa9238e6ef 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account_statuses_search.rb @@ -32,7 +32,7 @@ module AccountStatusesSearch return unless Chewy.enabled? statuses.without_reblogs.where(visibility: :public).find_in_batches do |batch| - PublicStatusesIndex.import(query: batch) + PublicStatusesIndex.import(batch) end end From bafa60f5b92d3d7900ae5c81b615cd91391a4f0f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 17:08:37 +0200 Subject: [PATCH 026/182] Fix bad search type heuristic (#26673) --- app/services/search_service.rb | 14 ++++++-------- spec/services/search_service_spec.rb | 9 --------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 4e1e7ea26e..40d82fc525 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -17,7 +17,7 @@ class SearchService < BaseService results.merge!(url_resource_results) unless url_resource.nil? || @offset.positive? || (@options[:type].present? && url_resource_symbol != @options[:type].to_sym) elsif @query.present? results[:accounts] = perform_accounts_search! if account_searchable? - results[:statuses] = perform_statuses_search! if full_text_searchable? + results[:statuses] = perform_statuses_search! if status_searchable? results[:hashtags] = perform_hashtags_search! if hashtag_searchable? end end @@ -79,18 +79,16 @@ class SearchService < BaseService url_resource.class.name.downcase.pluralize.to_sym end - def full_text_searchable? - return false unless Chewy.enabled? - - statuses_search? && !@account.nil? && !(@query.include?('@') && !@query.include?(' ')) + def status_searchable? + Chewy.enabled? && status_search? && @account.present? end def account_searchable? - account_search? && !(@query.include?('@') && @query.include?(' ')) + account_search? end def hashtag_searchable? - hashtag_search? && !@query.include?('@') + hashtag_search? end def account_search? @@ -101,7 +99,7 @@ class SearchService < BaseService @options[:type].blank? || @options[:type] == 'hashtags' end - def statuses_search? + def status_search? @options[:type].blank? || @options[:type] == 'statuses' end end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 123bd4f560..cb69af5f54 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -83,15 +83,6 @@ describe SearchService, type: :service do expect(Tag).to have_received(:search_for).with('tag', 10, 0, exclude_unreviewed: nil) expect(results).to eq empty_results.merge(hashtags: [tag]) end - - it 'does not include tag when starts with @ character' do - query = '@username' - allow(Tag).to receive(:search_for) - - results = subject.call(query, nil, 10) - expect(Tag).to_not have_received(:search_for) - expect(results).to eq empty_results - end end end end From e1b0d6bbe590b1d4f7bc90a528eeb20d89c499b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:10:58 +0200 Subject: [PATCH 027/182] Update dependency stoplight to v3.0.2 (#26698) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7a68249c28..49ada55d2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -731,7 +731,7 @@ GEM net-ssh (>= 2.8.0) stackprof (0.2.25) statsd-ruby (1.5.0) - stoplight (3.0.1) + stoplight (3.0.2) redlock (~> 1.0) strong_migrations (0.8.0) activerecord (>= 5.2) From 92c9bf731d68bead6358ae35c09413c78d65f4b2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 19:40:08 +0200 Subject: [PATCH 028/182] Change video compression parameters (#26631) --- app/models/media_attachment.rb | 16 ++++++---------- lib/paperclip/transcoder.rb | 10 ++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 7474b5653f..88abd93390 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -44,6 +44,7 @@ class MediaAttachment < ApplicationRecord MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px MAX_VIDEO_FRAME_RATE = 120 + MAX_VIDEO_FRAMES = 36_000 # Approx. 5 minutes at 120 fps IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heic .heif .avif).freeze VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze @@ -98,17 +99,12 @@ class MediaAttachment < ApplicationRecord convert_options: { output: { 'loglevel' => 'fatal', - 'movflags' => 'faststart', - 'pix_fmt' => 'yuv420p', - 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', - 'vsync' => 'cfr', + 'preset' => 'veryfast', 'c:v' => 'h264', - 'maxrate' => '1300K', - 'bufsize' => '1300K', - 'b:v' => '1300K', - 'frames:v' => 60 * 60 * 3, - 'crf' => 18, + 'c:a' => 'aac', + 'b:a' => '192k', 'map_metadata' => '-1', + 'frames:v' => MAX_VIDEO_FRAMES, }.freeze, }.freeze, }.freeze @@ -135,7 +131,7 @@ class MediaAttachment < ApplicationRecord convert_options: { output: { 'loglevel' => 'fatal', - :vf => 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease', + :vf => 'scale=\'min(640\, iw):min(640\, ih)\':force_original_aspect_ratio=decrease', }.freeze, }.freeze, format: 'png', diff --git a/lib/paperclip/transcoder.rb b/lib/paperclip/transcoder.rb index 0f2e30f7d5..b88cf662c2 100644 --- a/lib/paperclip/transcoder.rb +++ b/lib/paperclip/transcoder.rb @@ -4,6 +4,9 @@ module Paperclip # This transcoder is only to be used for the MediaAttachment model # to check when uploaded videos are actually gifv's class Transcoder < Paperclip::Processor + # This is the H.264 "High" value taken from https://www.dr-lex.be/info-stuff/videocalc.html + BITS_PER_PIXEL = 0.11 + def initialize(file, options = {}, attachment = nil) super @@ -38,8 +41,11 @@ module Paperclip @output_options['vframes'] = 1 when 'mp4' unless eligible_to_passthrough?(metadata) - @output_options['acodec'] = 'aac' - @output_options['strict'] = 'experimental' + bitrate = (metadata.width * metadata.height * 30 * BITS_PER_PIXEL) / 1_000 + + @output_options['b:v'] = "#{bitrate}k" + @output_options['maxrate'] = "#{bitrate + 192}k" + @output_options['bufsize'] = "#{bitrate * 5}k" if high_vfr?(metadata) @output_options['vsync'] = 'vfr' From a4130c41390eb8b828357d82b43c9d42e51b8755 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 19:49:38 +0200 Subject: [PATCH 029/182] Fix colors and typography on hashtag bar in web UI (#26666) --- .../mastodon/components/hashtag_bar.tsx | 6 +++--- .../styles/mastodon/components.scss | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx index 674c481b81..d45a6e20eb 100644 --- a/app/javascript/mastodon/components/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/hashtag_bar.tsx @@ -10,8 +10,8 @@ import { groupBy, minBy } from 'lodash'; import { getStatusContent } from './status_content'; -// About two lines on desktop -const VISIBLE_HASHTAGS = 7; +// Fit on a single line on desktop +const VISIBLE_HASHTAGS = 3; // Those types are not correct, they need to be replaced once this part of the state is typed export type TagLike = Record<{ name: string }>; @@ -210,7 +210,7 @@ const HashtagBar: React.FC<{ const revealedHashtags = expanded ? hashtags - : hashtags.slice(0, VISIBLE_HASHTAGS - 1); + : hashtags.slice(0, VISIBLE_HASHTAGS); return (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 6fa5c545ea..c07f95f564 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -9308,19 +9308,24 @@ noscript { display: flex; flex-wrap: wrap; font-size: 14px; + line-height: 18px; gap: 4px; + color: $darker-text-color; a { display: inline-flex; - color: $dark-text-color; + color: inherit; text-decoration: none; - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } + &:hover span { + text-decoration: underline; } } + + .link-button { + color: inherit; + font-size: inherit; + line-height: inherit; + padding: 0; + } } From 9a65f28e9eacd6b9282312fe40af87b4e92da3a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:28:45 +0200 Subject: [PATCH 030/182] New Crowdin translations (#2388) Co-authored-by: GitHub Actions --- .../flavours/glitch/locales/zh-TW.json | 68 ++++++++++++++++++- config/locales-glitch/zh-TW.yml | 8 +++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/locales/zh-TW.json b/app/javascript/flavours/glitch/locales/zh-TW.json index 059c9bd19b..221d8f9d68 100644 --- a/app/javascript/flavours/glitch/locales/zh-TW.json +++ b/app/javascript/flavours/glitch/locales/zh-TW.json @@ -16,11 +16,17 @@ "advanced_options.local-only.long": "不要傳遞給其他實例", "advanced_options.local-only.short": "僅限本地", "advanced_options.local-only.tooltip": "此嘟文僅限本地", + "advanced_options.threaded_mode.long": "發佈時自動打開回覆", "advanced_options.threaded_mode.short": "討論串模式", "advanced_options.threaded_mode.tooltip": "已啟用討論串模式", "boost_modal.missing_description": "此嘟文包含未加說明的媒體檔案", + "column.favourited_by": "誰按了最愛", + "column.heading": "雜項", + "column.reblogged_by": "被誰轉嘟", + "column.subheading": "其他選項", "column_header.profile": "個人檔案", "column_subheading.lists": "列表", + "column_subheading.navigation": "導覽", "community.column_settings.allow_local_only": "顯示僅限本地的嘟文", "compose.attach": "附加...", "compose.attach.doodle": "塗鴉", @@ -30,27 +36,66 @@ "compose.content-type.plain": "純文字", "compose_form.poll.multiple_choices": "允許多重選擇", "compose_form.poll.single_choice": "允許單一選擇", + "compose_form.spoiler": "將文字隱藏在內容警告後面", "confirmation_modal.do_not_ask_again": "不要再顯示確認訊息", "confirmations.deprecated_settings.confirm": "使用 Mastodon 偏好", + "confirmations.deprecated_settings.message": "您正在使用的某些特定於 glitch-soc 設備的 {app_settings} 已被 Mastodon {preferences} 所取代,並將被覆蓋:", + "confirmations.missing_media_description.confirm": "仍要張貼", "confirmations.missing_media_description.edit": "編輯媒體", "confirmations.missing_media_description.message": "至少有一個媒體附件缺少說明。 在發送嘟文之前,請考慮為視障人士在所有媒體附件加上說明。", "confirmations.unfilter.author": "作者", "confirmations.unfilter.confirm": "顯示", + "confirmations.unfilter.edit_filter": "編輯篩選器", "content-type.change": "內容類型", "direct.group_by_conversations": "以對話分組", "empty_column.follow_recommendations": "似乎未能為您產生任何建議。您可以嘗試使用搜尋來尋找您可能認識的人,或是探索熱門主題標籤。", + "endorsed_accounts_editor.endorsed_accounts": "受推薦帳號", + "favourite_modal.combo": "下次您可以按 {combo} 跳過", + "firehose.column_settings.allow_local_only": "在「全部」顯示僅限本地的貼文", "follow_recommendations.done": "完成", "follow_recommendations.heading": "跟隨您想檢視其嘟文的人!這裡有一些建議。", "follow_recommendations.lead": "來自您跟隨的人之嘟文將會按時間順序顯示在您的首頁時間軸上。不要害怕犯錯,您隨時都可以取消跟隨其他人!", + "getting_started.onboarding": "帶我四處看看", "home.column_settings.advanced": "進階設定", "home.column_settings.filter_regex": "以正規表達式進行過濾", + "home.column_settings.show_direct": "顯示私人提及", + "home.settings": "欄位設定", + "keyboard_shortcuts.bookmark": "到書籤", + "keyboard_shortcuts.secondary_toot": "使用次要隱私設定來發布嘟文", + "keyboard_shortcuts.toggle_collapse": "去折疊/展開嘟文", "media_gallery.sensitive": "敏感", + "moved_to_warning": "此帳戶已標記為移至 {moved_to_link},因此可能不接受新的追隨者。", + "navigation_bar.app_settings": "應用程式設定", + "navigation_bar.featured_users": "被推薦的使用者", + "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵", + "navigation_bar.misc": "雜項", + "notification.markForDeletion": "標記刪除", "notification_purge.btn_all": "選取全部", "notification_purge.btn_apply": "清除所選項目", "notification_purge.btn_invert": "反向選擇", "notification_purge.btn_none": "取消選取", + "notification_purge.start": "進入通知清理模式", + "notifications.marked_clear": "清除被選取的通知訊息", + "notifications.marked_clear_confirmation": "您確定要永久清除所有被選取的通知訊息嗎?", + "onboarding.done": "完成", + "onboarding.next": "下一個", + "onboarding.page_five.public_timelines": "本地時間軸顯示來自 {domain} 上所有人的公開貼文。聯合時間軸顯示 {domain} 上追隨的每個人發表的公開貼文。這些是公共時間軸,是發現新朋友的好方法。", + "onboarding.page_four.home": "首頁時間線會顯示你追隨的人發布的貼文。", + "onboarding.page_four.notifications": "當有人與您互動時會顯示在通知欄。", "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", + "onboarding.page_one.handle": "你的帳號在 {domain} ,所以你的帳號全名是 {handle}", + "onboarding.page_one.welcome": "歡迎來到 {domain} !", + "onboarding.page_six.admin": "您的站台管理者是 {admin} 。", + "onboarding.page_six.almost_done": "就快完成了…", + "onboarding.page_six.apps_available": "有適用於 iOS、Android 和其他平台的 {apps}。", "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.", + "onboarding.page_six.guidelines": "社群規範", + "onboarding.page_six.read_guidelines": "請閱讀 {domain} 的 {guidelines}!", + "onboarding.page_six.various_app": "手機應用程式", + "onboarding.page_three.profile": "編輯您的個人資料以更改您的頭像、個人簡介和顯示名稱。在那裡,您還會發現其他偏好設置。", + "onboarding.page_three.search": "使用搜索欄查找他人與主題標籤,例如 {illustration} 和 {introductions} 。要尋找其他站台的人,請使用他們的完整帳號名稱。", + "onboarding.page_two.compose": "從撰寫欄撰寫帖子。您可以使用下面的圖示上傳圖片、更改隱私設置以及添加內容警告。", + "onboarding.skip": "略過", "settings.always_show_spoilers_field": "永遠啟用內容警告欄位", "settings.auto_collapse": "自動折疊", "settings.auto_collapse_all": "全部", @@ -83,19 +128,23 @@ "settings.hicolor_privacy_icons.hint": "用明亮且易於區分的顏色顯示隱私圖示", "settings.image_backgrounds": "圖片背景", "settings.image_backgrounds_media": "預覽折疊嘟文的媒體檔案", - "settings.image_backgrounds_media_hint": "如果嘟文包含媒體檔案,使用的一個作為圖片背景", + "settings.image_backgrounds_media_hint": "如果嘟文包含媒體檔案,使用第一個作為圖片背景", "settings.image_backgrounds_users": "為折疊的嘟文加上圖片背景", + "settings.inline_preview_cards": "針對外部連接顯示內嵌的預覽卡", "settings.layout_opts": "版面選項", "settings.media": "媒體", "settings.media_fullwidth": "在媒體預覽中使用完整寬度", "settings.media_letterbox": "在媒體預覽加上黑邊", "settings.media_letterbox_hint": "在媒體預覽中縮小並加上黑邊以取代延展與裁切", "settings.media_reveal_behind_cw": "預設顯示隱藏在內容警告的敏感媒體檔案", + "settings.notifications.favicon_badge": "未讀通知網站圖示徽章", + "settings.notifications.favicon_badge.hint": "在網站圖示上增加一個未讀通知徽章", "settings.notifications.tab_badge": "未讀通知徽章", "settings.notifications.tab_badge.hint": "當通知列未打開時,在導引圖示中顯示未讀通知的徽章", "settings.notifications_opts": "通知選項", "settings.pop_in_left": "左邊", "settings.pop_in_player": "啟用彈出播放器", + "settings.pop_in_position": "彈出播放器位置:", "settings.pop_in_right": "右邊", "settings.preferences": "使用者偏好設定", "settings.prepend_cw_re": "回覆時在內容警告前添加 \"re:\"", @@ -105,6 +154,7 @@ "settings.rewrite_mentions_acct": "改寫為使用者名稱與網域(當使用者來自外部)", "settings.rewrite_mentions_no": "不要改寫提及", "settings.rewrite_mentions_username": "改寫為使用者名稱", + "settings.shared_settings_link": "使用者偏好設定", "settings.show_action_bar": "在折疊的嘟文顯示操作按鈕", "settings.show_content_type_choice": "在編寫嘟文時顯示內容類型選擇", "settings.show_reply_counter": "顯示回覆數量的估計值", @@ -113,12 +163,14 @@ "settings.side_arm_reply_mode": "當回覆一篇嘟文時,次要發出嘟文按鈕應該設為:", "settings.side_arm_reply_mode.copy": "複製回覆嘟文的隱私設置", "settings.side_arm_reply_mode.keep": "保持原本的隱私設定", + "settings.side_arm_reply_mode.restrict": "限制只能使用與回覆嘟文相同的隱私設置", "settings.status_icons": "嘟文圖示", "settings.status_icons_language": "語言指示器", "settings.status_icons_local_only": "僅限本地指示器", "settings.status_icons_media": "媒體與投票指示器", "settings.status_icons_reply": "回覆指示器", "settings.status_icons_visibility": "嘟文隱私指示器", + "settings.swipe_to_change_columns": "允許使用滑動手勢更改顯示欄位(僅限移動裝置)", "settings.tag_misleading_links": "標記誤導性的連結", "settings.tag_misleading_links.hint": "在每個未明確提及的連結添加帶有連結目標主機的視覺指示", "settings.wide_view": "寬廣模式(僅限桌面模式)", @@ -130,5 +182,17 @@ "status.has_video": "包含視訊檔案", "status.in_reply_to": "嘟文有回覆", "status.is_poll": "嘟文有投票", - "status.local_only": "只在此實例可見" + "status.local_only": "只在此實例可見", + "status.sensitive_toggle": "點擊查看", + "status.uncollapse": "展開", + "web_app_crash.change_your_settings": "修改你的 {settings}", + "web_app_crash.content": "您可以嘗試以下任一種方法:", + "web_app_crash.debug_info": "除錯資訊", + "web_app_crash.disable_addons": "禁用瀏覽器插件或內置翻譯工具", + "web_app_crash.issue_tracker": "問題追蹤系統", + "web_app_crash.reload": "重新載入", + "web_app_crash.reload_page": "{reload} 當前頁面", + "web_app_crash.report_issue": "到 {issuetracker} 回報問題", + "web_app_crash.settings": "設定", + "web_app_crash.title": "很抱歉,Mastodon 應用程序出現問題。" } diff --git a/config/locales-glitch/zh-TW.yml b/config/locales-glitch/zh-TW.yml index cb82c05261..99af36c9ec 100644 --- a/config/locales-glitch/zh-TW.yml +++ b/config/locales-glitch/zh-TW.yml @@ -1 +1,9 @@ +--- zh-TW: + appearance: + localization: + glitch_guide_link: https://crowdin.com/project/glitch-soc + glitch_guide_link_text: 對於 glitch-soc 來說也是如此! + auth: + captcha_confirmation: + title: 使用者驗證 From f8b5f05d440796970b761dafb4bfea1784884ecf Mon Sep 17 00:00:00 2001 From: Lukas Martini Date: Tue, 29 Aug 2023 09:14:44 +0200 Subject: [PATCH 031/182] Improve error messages when DeepL quota is exceeded (#26704) --- .../api/v1/statuses/translations_controller.rb | 10 +++++++++- config/locales/en.yml | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/statuses/translations_controller.rb b/app/controllers/api/v1/statuses/translations_controller.rb index 540b17d009..ec5ea5b85b 100644 --- a/app/controllers/api/v1/statuses/translations_controller.rb +++ b/app/controllers/api/v1/statuses/translations_controller.rb @@ -8,7 +8,15 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController before_action :set_translation rescue_from TranslationService::NotConfiguredError, with: :not_found - rescue_from TranslationService::UnexpectedResponseError, TranslationService::QuotaExceededError, TranslationService::TooManyRequestsError, with: :service_unavailable + rescue_from TranslationService::UnexpectedResponseError, with: :service_unavailable + + rescue_from TranslationService::QuotaExceededError do + render json: { error: I18n.t('translation.errors.quota_exceeded') }, status: 503 + end + + rescue_from TranslationService::TooManyRequestsError do + render json: { error: I18n.t('translation.errors.too_many_requests') }, status: 503 + end def create render json: @translation, serializer: REST::TranslationSerializer diff --git a/config/locales/en.yml b/config/locales/en.yml index 71121bb2e2..8bdfd1ec91 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1709,6 +1709,10 @@ en: default: "%b %d, %Y, %H:%M" month: "%b %Y" time: "%H:%M" + translation: + errors: + quota_exceeded: The server-wide usage quota for the translation service has been exceeded. + too_many_requests: There have been too many requests to the translation service recently. two_factor_authentication: add: Add disable: Disable 2FA From d2ee5b19004995d385e344c5b3216e29c8337094 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:16:18 +0200 Subject: [PATCH 032/182] Remove dead code from public.jsx (#26547) --- app/javascript/packs/public.jsx | 81 ++++----------------------------- 1 file changed, 9 insertions(+), 72 deletions(-) diff --git a/app/javascript/packs/public.jsx b/app/javascript/packs/public.jsx index 1d917d60ee..ae4a7410e1 100644 --- a/app/javascript/packs/public.jsx +++ b/app/javascript/packs/public.jsx @@ -7,8 +7,6 @@ import { defineMessages } from 'react-intl'; import { delegate } from '@rails/ujs'; import axios from 'axios'; -import escapeTextContentForBrowser from 'escape-html'; -import { createBrowserHistory } from 'history'; import { throttle } from 'lodash'; import { start } from '../mastodon/common'; @@ -48,23 +46,6 @@ window.addEventListener('message', e => { function loaded() { const { messages: localeData } = getLocale(); - const scrollToDetailedStatus = () => { - const history = createBrowserHistory(); - const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status'); - const location = history.location; - - if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) { - detailedStatuses[0].scrollIntoView(); - history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true }); - } - }; - - const getEmojiAnimationHandler = (swapTo) => { - return ({ target }) => { - target.src = target.getAttribute(swapTo); - }; - }; - const locale = document.documentElement.lang; const dateTimeFormat = new Intl.DateTimeFormat(locale, { @@ -158,27 +139,21 @@ function loaded() { const root = createRoot(content); root.render(); document.body.appendChild(content); - scrollToDetailedStatus(); }) .catch(error => { console.error(error); - scrollToDetailedStatus(); }); - } else { - scrollToDetailedStatus(); } - delegate(document, '#user_account_attributes_username', 'input', throttle(() => { - const username = document.getElementById('user_account_attributes_username'); - - if (username.value && username.value.length > 0) { - axios.get('/api/v1/accounts/lookup', { params: { acct: username.value } }).then(() => { - username.setCustomValidity(formatMessage(messages.usernameTaken)); + delegate(document, '#user_account_attributes_username', 'input', throttle(({ target }) => { + if (target.value && target.value.length > 0) { + axios.get('/api/v1/accounts/lookup', { params: { acct: target.value } }).then(() => { + target.setCustomValidity(formatMessage(messages.usernameTaken)); }).catch(() => { - username.setCustomValidity(''); + target.setCustomValidity(''); }); } else { - username.setCustomValidity(''); + target.setCustomValidity(''); } }, 500, { leading: false, trailing: true })); @@ -196,9 +171,6 @@ function loaded() { } }); - delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); - delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); - delegate(document, '.status__content__spoiler-link', 'click', function() { const statusEl = this.parentNode.parentNode; @@ -220,17 +192,6 @@ function loaded() { }); } -delegate(document, '#account_display_name', 'input', ({ target }) => { - const name = document.querySelector('.card .display-name strong'); - if (name) { - if (target.value) { - name.innerHTML = emojify(escapeTextContentForBrowser(target.value)); - } else { - name.textContent = target.dataset.default; - } - } -}); - delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => { const avatar = document.getElementById(target.id + '-preview'); const [file] = target.files || []; @@ -239,33 +200,6 @@ delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => { avatar.src = url; }); -const getProfileAvatarAnimationHandler = (swapTo) => { - //animate avatar gifs on the profile page when moused over - return ({ target }) => { - const swapSrc = target.getAttribute(swapTo); - //only change the img source if autoplay is off and the image src is actually different - if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) { - target.src = swapSrc; - } - }; -}; - -delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original')); - -delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static')); - -delegate(document, '#account_locked', 'change', ({ target }) => { - const lock = document.querySelector('.card .display-name i'); - - if (lock) { - if (target.checked) { - delete lock.dataset.hidden; - } else { - lock.dataset.hidden = 'true'; - } - } -}); - delegate(document, '.input-copy input', 'click', ({ target }) => { target.focus(); target.select(); @@ -325,6 +259,9 @@ delegate(document, '.sidebar__toggle__icon', 'keydown', e => { } }); +delegate(document, '.custom-emoji', 'mouseover', ({ target }) => target.src = target.getAttribute('data-original')); +delegate(document, '.custom-emoji', 'mouseout', ({ target }) => target.src = target.getAttribute('data-static')); + // Empty the honeypot fields in JS in case something like an extension // automatically filled them. delegate(document, '#registration_new_user,#new_user', 'submit', () => { From 5d8b2277486cc823724c41ad56719fc2afb9c121 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:17:57 +0200 Subject: [PATCH 033/182] Support webpacker live-reloading on Docker (#26419) --- .devcontainer/docker-compose.yml | 1 + Procfile.dev | 2 +- config/initializers/content_security_policy.rb | 3 ++- config/webpacker.yml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index a2658ea8ba..20aecd71d6 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -25,6 +25,7 @@ services: command: sleep infinity ports: - '127.0.0.1:3000:3000' + - '127.0.0.1:3035:3035' - '127.0.0.1:4000:4000' networks: - external_network diff --git a/Procfile.dev b/Procfile.dev index ba04fb661b..fbb2c2de23 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq stream: env PORT=4000 yarn run start -webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0 +webpack: bin/webpack-dev-server diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 98c4f541f3..59ac3bdea2 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -34,7 +34,8 @@ Rails.application.config.content_security_policy do |p| p.worker_src :self, :blob, assets_host if Rails.env.development? - webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{Webpacker.dev_server.host_with_port}" } + webpacker_public_host = ENV.fetch('WEBPACKER_DEV_SERVER_PUBLIC', Webpacker.config.dev_server[:public]) + webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{webpacker_public_host}" } p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host diff --git a/config/webpacker.yml b/config/webpacker.yml index 6fd0fa1a0c..f8462e53a0 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -58,7 +58,7 @@ development: # Reference: https://webpack.js.org/configuration/dev-server/ dev_server: https: false - host: localhost + host: 0.0.0.0 port: 3035 public: localhost:3035 hmr: false From c536148396d73211e9c94e8f68daedb4125b4b9b Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:20:36 +0200 Subject: [PATCH 034/182] Improve Codespaces port forwarding (#26400) --- .devcontainer/Dockerfile | 4 -- .devcontainer/codespaces/devcontainer.json | 49 ++++++++++++++++++++++ .devcontainer/devcontainer.json | 22 +++++++--- README.md | 34 +++++++++++---- config/environments/development.rb | 2 + 5 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 .devcontainer/codespaces/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f991036add..b3b1d97a24 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,10 +4,6 @@ FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye # Install Rails # RUN gem install rails webdrivers -# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service -# The value is a comma-separated list of allowed domains -ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev" - ARG NODE_VERSION="16" RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1" diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json new file mode 100644 index 0000000000..ca9156fdaa --- /dev/null +++ b/.devcontainer/codespaces/devcontainer.json @@ -0,0 +1,49 @@ +{ + "name": "Mastodon on GitHub Codespaces", + "dockerComposeFile": "../docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + + "features": { + "ghcr.io/devcontainers/features/sshd:1": {} + }, + + "runServices": ["app", "db", "redis"], + + "forwardPorts": [3000, 4000], + + "portsAttributes": { + "3000": { + "label": "web", + "onAutoForward": "notify" + }, + "4000": { + "label": "stream", + "onAutoForward": "silent" + } + }, + + "otherPortsAttributes": { + "onAutoForward": "silent" + }, + + "remoteEnv": { + "LOCAL_DOMAIN": "${localEnv:CODESPACE_NAME}-3000.app.github.dev", + "LOCAL_HTTPS": "true", + "STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev", + "DISABLE_FORGERY_REQUEST_PROTECTION": "true", + "ES_ENABLED": "", + "LIBRE_TRANSLATE_ENDPOINT": "" + }, + + "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", + "postCreateCommand": ".devcontainer/post-create.sh", + "waitFor": "postCreateCommand", + + "customizations": { + "vscode": { + "settings": {}, + "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"] + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ce14169aae..fa8d6542c1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "Mastodon", + "name": "Mastodon on local machine", "dockerComposeFile": "docker-compose.yml", "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", @@ -8,13 +8,23 @@ "ghcr.io/devcontainers/features/sshd:1": {} }, - "runServices": ["app", "db", "redis"], - "forwardPorts": [3000, 4000], - "containerEnv": { - "ES_ENABLED": "", - "LIBRE_TRANSLATE_ENDPOINT": "" + "portsAttributes": { + "3000": { + "label": "web", + "onAutoForward": "notify", + "requireLocalPort": true + }, + "4000": { + "label": "stream", + "onAutoForward": "silent", + "requireLocalPort": true + } + }, + + "otherPortsAttributes": { + "onAutoForward": "silent" }, "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", diff --git a/README.md b/README.md index 37cd3dfb46..e925bec519 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,13 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre ## Deployment -### Tech stack: +### Tech stack - **Ruby on Rails** powers the REST API and other web pages - **React.js** and Redux are used for the dynamic parts of the interface - **Node.js** powers the streaming API -### Requirements: +### Requirements - **PostgreSQL** 9.5+ - **Redis** 4+ @@ -74,6 +74,10 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. +## Development + +### Vagrant + A **Vagrant** configuration is included for development purposes. To use it, complete the following steps: - Install Vagrant and Virtualbox @@ -82,9 +86,11 @@ A **Vagrant** configuration is included for development purposes. To use it, com - Run `vagrant ssh -c "cd /vagrant && foreman start"` - Open `http://mastodon.local` in your browser +### MacOS + To set up **MacOS** for native development, complete the following steps: -- Install the latest stable Ruby version (use a ruby version manager for easy installation and management of ruby versions) +- Install the latest stable Ruby version (use a Ruby version manager for easy installation and management of Ruby versions) - Run `brew install postgresql@14` - Run `brew install redis` - Run `brew install imagemagick` @@ -94,15 +100,27 @@ To set up **MacOS** for native development, complete the following steps: - Run `bundle exec rails db:setup` (optionally prepend `RAILS_ENV=development` to target the dev environment) - Finally, run `overmind start -f Procfile.dev` -### Getting Started with GitHub Codespaces +### Docker -To get started, create a codespace for this repository by clicking this 👇 +For development with **Docker**, complete the following steps: -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283) +- Install Docker Desktop +- Run `docker compose -f .devcontainer/docker-compose.yml up -d` +- Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh` +- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app foreman start -f Procfile.dev` -A codespace will open in a web-based version of Visual Studio Code. The [dev container](.devcontainer/devcontainer.json) is fully configured with the software needed for this project. +If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers). -**Note**: Dev containers are an open spec that is supported by [GitHub Codespaces](https://github.com/codespaces) and [other tools](https://containers.dev/supporting). +### GitHub Codespaces + +To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project.. + +- Click this button to create a new codespace:
+ [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json) +- Wait for the environment to build. This will take a few minutes. +- When the editor is ready, run `foreman start -f Procfile.dev` in the terminal. +- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon. +- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_. ## Contributing diff --git a/config/environments/development.rb b/config/environments/development.rb index 31a3962458..9a6637bdb9 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -35,6 +35,8 @@ Rails.application.configure do config.cache_store = :null_store end + config.action_controller.forgery_protection_origin_check = ENV['DISABLE_FORGERY_REQUEST_PROTECTION'].nil? + ActiveSupport::Logger.new(STDOUT).tap do |logger| logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) From e7472ecd2a8c2392e9364e5176aef8773a32406c Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 10:29:07 +0200 Subject: [PATCH 035/182] Add debug logging on signature verification failure (#26637) --- app/controllers/concerns/signature_verification.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 1d27c92c8c..b0c4fff8bc 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -119,6 +119,8 @@ module SignatureVerification private def fail_with!(message, **options) + Rails.logger.warn { "Signature verification failed: #{message}" } + @signature_verification_failure_reason = { error: message }.merge(options) @signed_request_actor = nil end From 425af3ef0f4e8efc090ff0618fda5de733791638 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:50:27 +0200 Subject: [PATCH 036/182] Update dependency axios to v1.5.0 (#26680) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3e9c9abf77..0f34ad3551 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3344,9 +3344,9 @@ axe-core@^4.6.2: integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== axios@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" + integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" From dec7a90989c823dba7f6322c3359c3cd861812e9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:53:01 +0200 Subject: [PATCH 037/182] Update dependency aws-sdk-s3 to v1.133.0 (#26616) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 49ada55d2a..f26856bf94 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -124,8 +124,8 @@ GEM attr_required (1.0.1) awrence (1.2.1) aws-eventstream (1.2.0) - aws-partitions (1.793.0) - aws-sdk-core (3.180.3) + aws-partitions (1.809.0) + aws-sdk-core (3.181.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) @@ -133,8 +133,8 @@ GEM aws-sdk-kms (1.71.0) aws-sdk-core (~> 3, >= 3.177.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.132.1) - aws-sdk-core (~> 3, >= 3.179.0) + aws-sdk-s3 (1.133.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.6) aws-sigv4 (1.6.0) From 1b52b02c1c8cd8c892cea374e46bcd2ce183d023 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 03:56:19 -0500 Subject: [PATCH 038/182] Fix bug with favourited view on Toots only showing latest favouriting accounts (#26577) --- .../mastodon/actions/interactions.js | 58 ++++++++++++++++++- .../mastodon/features/favourites/index.jsx | 25 +++++--- .../mastodon/reducers/user_lists.js | 15 ++++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js index 092a67ea75..1ffd23db53 100644 --- a/app/javascript/mastodon/actions/interactions.js +++ b/app/javascript/mastodon/actions/interactions.js @@ -1,5 +1,6 @@ -import api from '../api'; +import api, { getLinks } from '../api'; +import { fetchRelationships } from './accounts'; import { importFetchedAccounts, importFetchedStatus } from './importer'; export const REBLOG_REQUEST = 'REBLOG_REQUEST'; @@ -26,6 +27,10 @@ export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST'; export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS'; export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL'; +export const FAVOURITES_EXPAND_REQUEST = 'FAVOURITES_EXPAND_REQUEST'; +export const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS'; +export const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL'; + export const PIN_REQUEST = 'PIN_REQUEST'; export const PIN_SUCCESS = 'PIN_SUCCESS'; export const PIN_FAIL = 'PIN_FAIL'; @@ -308,8 +313,10 @@ export function fetchFavourites(id) { dispatch(fetchFavouritesRequest(id)); api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFavouritesSuccess(id, response.data)); + dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFavouritesFail(id, error)); }); @@ -323,17 +330,62 @@ export function fetchFavouritesRequest(id) { }; } -export function fetchFavouritesSuccess(id, accounts) { +export function fetchFavouritesSuccess(id, accounts, next) { return { type: FAVOURITES_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchFavouritesFail(id, error) { return { type: FAVOURITES_FETCH_FAIL, + id, + error, + }; +} + +export function expandFavourites(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'favourited_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandFavouritesRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandFavouritesFail(id, error))); + }; +} + +export function expandFavouritesRequest(id) { + return { + type: FAVOURITES_EXPAND_REQUEST, + id, + }; +} + +export function expandFavouritesSuccess(id, accounts, next) { + return { + type: FAVOURITES_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandFavouritesFail(id, error) { + return { + type: FAVOURITES_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/mastodon/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.jsx index bfde78708e..b8ba948728 100644 --- a/app/javascript/mastodon/features/favourites/index.jsx +++ b/app/javascript/mastodon/features/favourites/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchFavourites } from 'mastodon/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; import ColumnHeader from 'mastodon/components/column_header'; import { Icon } from 'mastodon/components/icon'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; @@ -21,7 +23,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true), }); class Favourites extends ImmutablePureComponent { @@ -30,6 +34,8 @@ class Favourites extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -40,18 +46,16 @@ class Favourites extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps (nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchFavourites(nextProps.params.statusId)); - } - } - handleRefresh = () => { this.props.dispatch(fetchFavourites(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandFavourites(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -75,6 +79,9 @@ class Favourites extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index e33d365c9c..cc9a8b19a5 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -46,7 +46,12 @@ import { } from '../actions/blocks'; import { REBLOGS_FETCH_SUCCESS, + FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, + FAVOURITES_FETCH_FAIL, + FAVOURITES_EXPAND_REQUEST, + FAVOURITES_EXPAND_SUCCESS, + FAVOURITES_EXPAND_FAIL, } from '../actions/interactions'; import { MUTES_FETCH_REQUEST, @@ -136,7 +141,15 @@ export default function userLists(state = initialState, action) { case REBLOGS_FETCH_SUCCESS: return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_EXPAND_SUCCESS: + return appendToList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_FETCH_REQUEST: + case FAVOURITES_EXPAND_REQUEST: + return state.setIn(['favourited_by', action.id, 'isLoading'], true); + case FAVOURITES_FETCH_FAIL: + case FAVOURITES_EXPAND_FAIL: + return state.setIn(['favourited_by', action.id, 'isLoading'], false); case NOTIFICATIONS_UPDATE: return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; case FOLLOW_REQUESTS_FETCH_SUCCESS: From 47a4923157eeb5a84f879321072d7b387cc5add3 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 14:06:22 +0200 Subject: [PATCH 039/182] Fix N+1 in `tootctl search deploy` (#26710) --- .../public_statuses_index_importer.rb | 23 ++++++------------- app/lib/importer/statuses_index_importer.rb | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb index 8e36e36f90..72d02318b1 100644 --- a/app/lib/importer/public_statuses_index_importer.rb +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -2,23 +2,14 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter def import! - indexable_statuses_scope.find_in_batches(batch_size: @batch_size) do |batch| - in_work_unit(batch.map(&:status_id)) do |status_ids| + scope.select(:id).find_in_batches(batch_size: @batch_size) do |batch| + in_work_unit(batch.pluck(:id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body end - indexed = 0 - deleted = 0 - - bulk.map! do |entry| - if entry[:index] - indexed += 1 - else - deleted += 1 - end - entry - end + indexed = bulk.count { |entry| entry[:index] } + deleted = bulk.count { |entry| entry[:delete] } Chewy::Index::Import::BulkRequest.new(index).perform(bulk) @@ -35,7 +26,7 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter PublicStatusesIndex end - def indexable_statuses_scope - Status.indexable.select('"statuses"."id", COALESCE("statuses"."reblog_of_id", "statuses"."id") AS status_id') + def scope + Status.indexable end end diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index b0721c2e02..0277cd0ef5 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -14,7 +14,7 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body end indexed = 0 From 378b3dcddfb17bc942041b584985a79802869c98 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 07:42:20 -0500 Subject: [PATCH 040/182] Fix bug with reblogged view on Toots only showing latest reblogging accounts (#26574) --- .../mastodon/actions/interactions.js | 55 ++++++++++++++++++- .../mastodon/features/reblogs/index.jsx | 27 +++++---- .../mastodon/reducers/user_lists.js | 15 ++++- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js index 1ffd23db53..7d0144438a 100644 --- a/app/javascript/mastodon/actions/interactions.js +++ b/app/javascript/mastodon/actions/interactions.js @@ -7,6 +7,10 @@ export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_FAIL = 'REBLOG_FAIL'; +export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; +export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; +export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; + export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; @@ -278,8 +282,10 @@ export function fetchReblogs(id) { dispatch(fetchReblogsRequest(id)); api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchReblogsSuccess(id, response.data)); + dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchReblogsFail(id, error)); }); @@ -293,17 +299,62 @@ export function fetchReblogsRequest(id) { }; } -export function fetchReblogsSuccess(id, accounts) { +export function fetchReblogsSuccess(id, accounts, next) { return { type: REBLOGS_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchReblogsFail(id, error) { return { type: REBLOGS_FETCH_FAIL, + id, + error, + }; +} + +export function expandReblogs(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'reblogged_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandReblogsRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandReblogsFail(id, error))); + }; +} + +export function expandReblogsRequest(id) { + return { + type: REBLOGS_EXPAND_REQUEST, + id, + }; +} + +export function expandReblogsSuccess(id, accounts, next) { + return { + type: REBLOGS_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandReblogsFail(id, error) { + return { + type: REBLOGS_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/mastodon/features/reblogs/index.jsx b/app/javascript/mastodon/features/reblogs/index.jsx index 8bcef863f2..0c4e6dbb93 100644 --- a/app/javascript/mastodon/features/reblogs/index.jsx +++ b/app/javascript/mastodon/features/reblogs/index.jsx @@ -8,9 +8,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; +import { debounce } from 'lodash'; + import { Icon } from 'mastodon/components/icon'; -import { fetchReblogs } from '../../actions/interactions'; +import { fetchReblogs, expandReblogs } from '../../actions/interactions'; import ColumnHeader from '../../components/column_header'; import { LoadingIndicator } from '../../components/loading_indicator'; import ScrollableList from '../../components/scrollable_list'; @@ -22,7 +24,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'isLoading'], true), }); class Reblogs extends ImmutablePureComponent { @@ -31,6 +35,8 @@ class Reblogs extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -39,20 +45,18 @@ class Reblogs extends ImmutablePureComponent { if (!this.props.accountIds) { this.props.dispatch(fetchReblogs(this.props.params.statusId)); } - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchReblogs(nextProps.params.statusId)); - } - } + }; handleRefresh = () => { this.props.dispatch(fetchReblogs(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandReblogs(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -76,6 +80,9 @@ class Reblogs extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index cc9a8b19a5..089899398e 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -45,7 +45,12 @@ import { BLOCKS_EXPAND_FAIL, } from '../actions/blocks'; import { + REBLOGS_FETCH_REQUEST, REBLOGS_FETCH_SUCCESS, + REBLOGS_FETCH_FAIL, + REBLOGS_EXPAND_REQUEST, + REBLOGS_EXPAND_SUCCESS, + REBLOGS_EXPAND_FAIL, FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, FAVOURITES_FETCH_FAIL, @@ -139,7 +144,15 @@ export default function userLists(state = initialState, action) { case FOLLOWING_EXPAND_FAIL: return state.setIn(['following', action.id, 'isLoading'], false); case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_EXPAND_SUCCESS: + return appendToList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_FETCH_REQUEST: + case REBLOGS_EXPAND_REQUEST: + return state.setIn(['reblogged_by', action.id, 'isLoading'], true); + case REBLOGS_FETCH_FAIL: + case REBLOGS_EXPAND_FAIL: + return state.setIn(['reblogged_by', action.id, 'isLoading'], false); case FAVOURITES_FETCH_SUCCESS: return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); case FAVOURITES_EXPAND_SUCCESS: From e5da7a6c53d003f46f7f02227e66b67f7e5e597c Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 17:51:13 +0200 Subject: [PATCH 041/182] Change private statuses index to index without crutches (#26713) --- app/chewy/statuses_index.rb | 31 ++------------------ app/lib/importer/statuses_index_importer.rb | 2 +- app/models/concerns/status_search_concern.rb | 28 +++++++----------- app/models/poll.rb | 1 + app/models/status.rb | 6 ++++ 5 files changed, 21 insertions(+), 47 deletions(-) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6d33521051..2be7e45250 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -40,40 +40,13 @@ class StatusesIndex < Chewy::Index }, } - # We do not use delete_if option here because it would call a method that we - # expect to be called with crutches without crutches, causing n+1 queries - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll, :preview_cards) - - crutch :mentions do |collection| - data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :favourites do |collection| - data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :reblogs do |collection| - data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :bookmarks do |collection| - data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :votes do |collection| - data = ::PollVote.joins(:poll).where(poll: { status_id: collection.map(&:id) }).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } - field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) + field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:created_at, type: 'date') diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index 0277cd0ef5..285ddc871a 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -14,7 +14,7 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: index.adapter.default_scope.where(id: status_ids)).bulk_body end indexed = 0 diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status_search_concern.rb index 21048b5682..3ef45754ab 100644 --- a/app/models/concerns/status_search_concern.rb +++ b/app/models/concerns/status_search_concern.rb @@ -7,26 +7,20 @@ module StatusSearchConcern scope :indexable, -> { without_reblogs.where(visibility: :public).joins(:account).where(account: { indexable: true }) } end - def searchable_by(preloaded = nil) - ids = [] + def searchable_by + @searchable_by ||= begin + ids = [] - ids << account_id if local? + ids << account_id if local? - if preloaded.nil? - ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) - ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) - ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) - ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) - ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? - else - ids += preloaded.mentions[id] || [] - ids += preloaded.favourites[id] || [] - ids += preloaded.reblogs[id] || [] - ids += preloaded.bookmarks[id] || [] - ids += preloaded.votes[id] || [] + ids += local_mentioned.pluck(:id) + ids += local_favorited.pluck(:id) + ids += local_reblogged.pluck(:id) + ids += local_bookmarked.pluck(:id) + ids += preloadable_poll.local_voters.pluck(:id) if preloadable_poll.present? + + ids.uniq end - - ids.uniq end def searchable_text diff --git a/app/models/poll.rb b/app/models/poll.rb index 74a77978b9..efa625eb5b 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -28,6 +28,7 @@ class Poll < ApplicationRecord has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all has_many :voters, -> { group('accounts.id') }, through: :votes, class_name: 'Account', source: :account + has_many :local_voters, -> { group('accounts.id').merge(Account.local) }, through: :votes, class_name: 'Account', source: :account has_many :notifications, as: :activity, dependent: :destroy diff --git a/app/models/status.rb b/app/models/status.rb index 760b8ec33e..1c41ef1d52 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -72,6 +72,12 @@ class Status < ApplicationRecord has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify + # Those associations are used for the private search index + has_many :local_mentioned, -> { merge(Account.local) }, through: :active_mentions, source: :account + has_many :local_favorited, -> { merge(Account.local) }, through: :favourites, source: :account + has_many :local_reblogged, -> { merge(Account.local) }, through: :reblogs, source: :account + has_many :local_bookmarked, -> { merge(Account.local) }, through: :bookmarks, source: :account + has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards From e04379d3e178928228d3fb8fd871daf8c0c0273b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:34:07 +0200 Subject: [PATCH 042/182] Update dependency webmock to v3.19.1 (#26722) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f26856bf94..7e5dac0318 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,7 +109,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) airbrussh (1.4.1) @@ -795,7 +795,7 @@ GEM webfinger (1.2.0) activesupport httpclient (>= 2.4) - webmock (3.18.1) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) From 6fc874631a2bb6ef68774edc5a295688254388f6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:44:14 +0200 Subject: [PATCH 043/182] Update dependency postcss to v8.4.29 (#26720) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0f34ad3551..0a7c840dd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9625,9 +9625,9 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.2.15, postcss@^8.4.24, postcss@^8.4.25: - version "8.4.28" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.28.tgz#c6cc681ed00109072816e1557f889ef51cf950a5" - integrity sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw== + version "8.4.29" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" + integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" From 3c1ef9b54860d47800df01c6102df752313da138 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:45:13 +0200 Subject: [PATCH 044/182] Update dependency chewy to v7.3.4 (#26717) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7e5dac0318..f1a61c5e09 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,7 +203,7 @@ GEM activesupport cbor (0.5.9.6) charlock_holmes (0.7.7) - chewy (7.3.3) + chewy (7.3.4) activesupport (>= 5.2) elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch-dsl From d525fd6d877a344b3b79ec5823b26ec38b61591f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 30 Aug 2023 16:07:26 +0200 Subject: [PATCH 045/182] Change `language:` to attempt to match to a known value in search (#26663) --- app/lib/search_query_transformer.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index f1fc687e47..86e3f50005 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -98,7 +98,7 @@ class SearchQueryTransformer < Parslet::Transform when 'language' @filter = :language @type = :term - @term = term + @term = language_code_from_term(term) when 'from' @filter = :account_id @type = :term @@ -137,6 +137,22 @@ class SearchQueryTransformer < Parslet::Transform # an ID that does not exist account&.id || -1 end + + def language_code_from_term(term) + language_code = term + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + language_code = term.downcase + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + language_code = term.split(/[_-]/).first.downcase + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + term + end end rule(clause: subtree(:clause)) do From 4452d8dad567f8c459d4edb2cb1967c9f8e35cd7 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 16:29:52 +0200 Subject: [PATCH 046/182] Fix sign up steps progress layout in right-to-left locales (#26728) --- app/javascript/styles/mastodon/forms.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index beb45ab6e9..a7079c1457 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1185,14 +1185,14 @@ code { } li:first-child .label { - left: auto; inset-inline-start: 0; + inset-inline-end: auto; text-align: start; transform: none; } li:last-child .label { - left: auto; + inset-inline-start: auto; inset-inline-end: 0; text-align: end; transform: none; From 4d9dbcc5f07fada1bbbef846434298bf1e0e5655 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 17:36:16 +0200 Subject: [PATCH 047/182] Change text extraction in `PlainTextFormatter` to be faster (#26727) --- app/lib/plain_text_formatter.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/lib/plain_text_formatter.rb b/app/lib/plain_text_formatter.rb index 8eac730bec..d1ff6808b2 100644 --- a/app/lib/plain_text_formatter.rb +++ b/app/lib/plain_text_formatter.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class PlainTextFormatter - include ActionView::Helpers::TextHelper - NEWLINE_TAGS_RE = %r{(
|
|

)+} attr_reader :text, :local @@ -18,7 +16,10 @@ class PlainTextFormatter if local? text else - html_entities.decode(strip_tags(insert_newlines)).chomp + node = Nokogiri::HTML.fragment(insert_newlines) + # Elements that are entirely removed with our Sanitize config + node.xpath('.//iframe|.//math|.//noembed|.//noframes|.//noscript|.//plaintext|.//script|.//style|.//svg|.//xmp').remove + node.text.chomp end end @@ -27,8 +28,4 @@ class PlainTextFormatter def insert_newlines text.gsub(NEWLINE_TAGS_RE) { |match| "#{match}\n" } end - - def html_entities - HTMLEntities.new - end end From 1097ca53778c92b5907f1cf96201f8f5f85b45af Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:49:34 +0200 Subject: [PATCH 048/182] Update dependency glob to v10.3.4 (#26734) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0a7c840dd5..ec5566be39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6231,9 +6231,9 @@ glob-parent@^6.0.2: is-glob "^4.0.3" glob@^10.2.5, glob@^10.2.6: - version "10.3.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" - integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + version "10.3.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.4.tgz#c85c9c7ab98669102b6defda76d35c5b1ef9766f" + integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== dependencies: foreground-child "^3.1.0" jackspeak "^2.0.3" @@ -7336,9 +7336,9 @@ iterator.prototype@^1.1.0: reflect.getprototypeof "^1.0.3" jackspeak@^2.0.3: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6" - integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.1.tgz#ce2effa4c458e053640e61938865a5b5fae98456" + integrity sha512-4iSY3Bh1Htv+kLhiiZunUhQ+OYXIn0ze3ulq8JeWrFKmhPAJSySV2+kdtRh2pGcCeF0s6oR8Oc+pYZynJj4t8A== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: @@ -8192,9 +8192,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" - integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== lz-string@^1.5.0: version "1.5.0" @@ -8526,9 +8526,9 @@ minipass@^5.0.0: integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" - integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== + version "7.0.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== minizlib@^2.1.1: version "2.1.2" @@ -11345,7 +11345,6 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From a76a99b2bba4abbfe110b8c5ffc88a3e0d4d0c10 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:52:23 +0200 Subject: [PATCH 049/182] Update dependency webpack-bundle-analyzer to v4.9.1 (#26733) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 74 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/yarn.lock b/yarn.lock index ec5566be39..89b11e8e2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2913,16 +2913,16 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.0.4, acorn@^8.8.2: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -8100,6 +8100,16 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + lodash.get@^4.0: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -8110,6 +8120,11 @@ lodash.has@^4.0: resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" integrity sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g== +lodash.invokemap@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz#1748cda5d8b0ef8369c4eb3ec54c21feba1f2d62" + integrity sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w== + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -8135,6 +8150,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.pullall@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.pullall/-/lodash.pullall-4.2.0.tgz#9d98b8518b7c965b0fae4099bd9fb7df8bbf38ba" + integrity sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -8150,6 +8170,11 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== +lodash.uniqby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" + integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww== + lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -10905,14 +10930,14 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446" + integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA== dependencies: "@polka/url" "^1.0.0-next.20" mrmime "^1.0.0" - totalist "^1.0.0" + totalist "^3.0.0" sisteransi@^1.0.5: version "1.0.5" @@ -11831,10 +11856,10 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^4.1.2: version "4.1.3" @@ -12387,19 +12412,26 @@ webpack-assets-manifest@^4.0.6: webpack-sources "^1.0" webpack-bundle-analyzer@^4.8.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz#fc093c4ab174fd3dcbd1c30b763f56d10141209d" - integrity sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw== + version "4.9.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz#d00bbf3f17500c10985084f22f1a2bf45cb2f09d" + integrity sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w== dependencies: "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + is-plain-object "^5.0.0" + lodash.debounce "^4.0.8" + lodash.escape "^4.0.1" + lodash.flatten "^4.4.0" + lodash.invokemap "^4.6.0" + lodash.pullall "^4.2.0" + lodash.uniqby "^4.7.0" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" webpack-cli@^3.3.12: From 9c5bcaf188331f6848bcb0cdec94858fb4beae5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:23:24 +0200 Subject: [PATCH 050/182] Update dependency @babel/preset-env to v7.22.14 (#26732) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 224 +++++++++++++++++++++++++++--------------------------- 1 file changed, 111 insertions(+), 113 deletions(-) diff --git a/yarn.lock b/yarn.lock index 89b11e8e2b..880ded9773 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,7 +29,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== @@ -44,7 +44,15 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": +"@babel/code-frame@^7.22.5": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== @@ -113,7 +121,7 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.11": +"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz#4078686740459eeb4af3494a273ac09148dfb213" integrity sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ== @@ -128,21 +136,6 @@ "@babel/helper-split-export-declaration" "^7.22.6" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" - integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" @@ -292,7 +285,16 @@ "@babel/traverse" "^7.22.11" "@babel/types" "^7.22.11" -"@babel/highlight@^7.22.10", "@babel/highlight@^7.22.5": +"@babel/highlight@^7.22.10", "@babel/highlight@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" + integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/highlight@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== @@ -306,11 +308,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== -"@babel/parser@^7.22.11", "@babel/parser@^7.22.5": +"@babel/parser@^7.22.11": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== +"@babel/parser@^7.22.5": + version "7.22.14" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.14.tgz#c7de58e8de106e88efca42ce17f0033209dfd245" + integrity sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -487,10 +494,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8" - integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g== +"@babel/plugin-transform-async-generator-functions@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz#dbe3b1ff5a52e2e5edc4b19a60d325a675ed2649" + integrity sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" @@ -528,12 +535,12 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" - integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== +"@babel/plugin-transform-class-static-block@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz#dc8cc6e498f55692ac6b4b89e56d87cec766c974" + integrity sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" @@ -582,10 +589,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" - integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== +"@babel/plugin-transform-dynamic-import@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz#2c7722d2a5c01839eaf31518c6ff96d408e447aa" + integrity sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" @@ -598,10 +605,10 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" - integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== +"@babel/plugin-transform-export-namespace-from@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz#b3c84c8f19880b6c7440108f8929caf6056db26c" + integrity sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" @@ -622,10 +629,10 @@ "@babel/helper-function-name" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" - integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== +"@babel/plugin-transform-json-strings@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz#689a34e1eed1928a40954e37f74509f48af67835" + integrity sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" @@ -637,10 +644,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" - integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== +"@babel/plugin-transform-logical-assignment-operators@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz#24c522a61688bde045b7d9bc3c2597a4d948fc9c" + integrity sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -669,22 +676,13 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== - dependencies: - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - -"@babel/plugin-transform-modules-systemjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" - integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== +"@babel/plugin-transform-modules-systemjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" + integrity sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.9" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5" @@ -711,7 +709,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== @@ -719,21 +717,21 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" - integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== +"@babel/plugin-transform-numeric-separator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz#498d77dc45a6c6db74bb829c02a01c1d719cbfbd" + integrity sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" - integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== +"@babel/plugin-transform-object-rest-spread@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz#dbbb06ce783cd994a8f430d8cefa553e9b42ca62" + integrity sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw== dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.22.5" @@ -746,18 +744,18 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.5" -"@babel/plugin-transform-optional-catch-binding@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" - integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== +"@babel/plugin-transform-optional-catch-binding@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz#461cc4f578a127bb055527b3e77404cad38c08e0" + integrity sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a" - integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g== +"@babel/plugin-transform-optional-chaining@^7.22.12", "@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz#d7ebf6a88cd2f4d307b0e000ab630acd8124b333" + integrity sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -778,13 +776,13 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" - integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== +"@babel/plugin-transform-private-property-in-object@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz#ad45c4fc440e9cb84c718ed0906d96cf40f9a4e1" + integrity sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" @@ -948,9 +946,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.22.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f" - integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A== + version "7.22.14" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.14.tgz#1cbb468d899f64fa71c53446f13b7ff8c0005cc1" + integrity sha512-daodMIoVo+ol/g+//c/AH+szBkFj4STQUikvBijRGL72Ph+w+AMTSh55DUETe8KJlPlDT1k/mp7NBfOuiWmoig== dependencies: "@babel/compat-data" "^7.22.9" "@babel/helper-compilation-targets" "^7.22.10" @@ -978,41 +976,41 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.10" + "@babel/plugin-transform-async-generator-functions" "^7.22.11" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" "@babel/plugin-transform-block-scoping" "^7.22.10" "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.11" "@babel/plugin-transform-classes" "^7.22.6" "@babel/plugin-transform-computed-properties" "^7.22.5" "@babel/plugin-transform-destructuring" "^7.22.10" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.11" "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.11" "@babel/plugin-transform-for-of" "^7.22.5" "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.11" "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" "@babel/plugin-transform-member-expression-literals" "^7.22.5" "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.11" + "@babel/plugin-transform-modules-systemjs" "^7.22.11" "@babel/plugin-transform-modules-umd" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" - "@babel/plugin-transform-numeric-separator" "^7.22.5" - "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" + "@babel/plugin-transform-numeric-separator" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.22.11" "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.10" + "@babel/plugin-transform-optional-catch-binding" "^7.22.11" + "@babel/plugin-transform-optional-chaining" "^7.22.12" "@babel/plugin-transform-parameters" "^7.22.5" "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.11" "@babel/plugin-transform-property-literals" "^7.22.5" "@babel/plugin-transform-regenerator" "^7.22.10" "@babel/plugin-transform-reserved-words" "^7.22.5" @@ -1026,7 +1024,7 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.11" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" babel-plugin-polyfill-regenerator "^0.5.2" @@ -1125,7 +1123,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== @@ -1143,7 +1141,7 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5": +"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5", "@babel/types@^7.4.4": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2" integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg== @@ -3932,9 +3930,9 @@ caniuse-lite@^1.0.30001502: integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== caniuse-lite@^1.0.30001517: - version "1.0.30001522" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" - integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== + version "1.0.30001524" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz#1e14bce4f43c41a7deaeb5ebfe86664fe8dadb80" + integrity sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA== caniuse-lite@^1.0.30001520: version "1.0.30001520" @@ -4335,11 +4333,11 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.31.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" - integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== + version "3.32.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.1.tgz#55f9a7d297c0761a8eb1d31b593e0f5b6ffae964" + integrity sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA== dependencies: - browserslist "^4.21.9" + browserslist "^4.21.10" core-js@^2.5.0: version "2.6.12" @@ -5085,9 +5083,9 @@ electron-to-chromium@^1.4.428: integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== electron-to-chromium@^1.4.477: - version "1.4.500" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz#7dd05fdfbe02ed34b9f6099cfe01407b473d5af7" - integrity sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A== + version "1.4.505" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.505.tgz#00571ade5975b58413f0f56a665b065bfc29cdfc" + integrity sha512-0A50eL5BCCKdxig2SsCXhpuztnB9PfUgRMojj5tMvt8O54lbwz3t6wNgnpiTRosw5QjlJB7ixhVyeg8daLQwSQ== elliptic@^6.5.3: version "6.5.4" From e26e4702acadc05fca8bb550f98b4186b04d335d Mon Sep 17 00:00:00 2001 From: gunchleoc Date: Thu, 31 Aug 2023 12:17:10 +0200 Subject: [PATCH 051/182] Add suggestion for secure cyphers to nginx.conf (#26349) --- dist/nginx.conf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dist/nginx.conf b/dist/nginx.conf index 39fa58e50d..5bb9903864 100644 --- a/dist/nginx.conf +++ b/dist/nginx.conf @@ -36,7 +36,11 @@ server { server_name example.com; ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; + + # You can use https://ssl-config.mozilla.org/ to generate your cipher set. + # We recommend their "Intermediate" level. + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; From 2c0eaf50f6441da34783d982519889818901d467 Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Thu, 31 Aug 2023 12:18:46 +0200 Subject: [PATCH 052/182] Add an explanation banner on switching to single column mode (#26019) --- .../features/ui/components/navigation_panel.jsx | 16 +++++++++++----- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/locales/fr.json | 1 + app/javascript/styles/mastodon/components.scss | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index d36abf8f17..8006ca89a2 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -31,6 +31,7 @@ const messages = defineMessages({ about: { id: 'navigation_bar.about', defaultMessage: 'About' }, search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, + openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' }, }); class NavigationPanel extends Component { @@ -57,12 +58,17 @@ class NavigationPanel extends Component {
- {transientSingleColumn && ( - - {intl.formatMessage(messages.advancedInterface)} - + {transientSingleColumn ? ( +
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +
+ ) : ( +
)} -
{signedIn && ( diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5871b08def..90bb9616f0 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -411,6 +411,7 @@ "navigation_bar.lists": "Lists", "navigation_bar.logout": "Logout", "navigation_bar.mutes": "Muted users", + "navigation_bar.opened_in_classic_interface": "Posts, accounts, and other specific pages are opened by default in the classic web interface.", "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned posts", "navigation_bar.preferences": "Preferences", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 2bef3bb4b3..116ed66d03 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -409,6 +409,7 @@ "navigation_bar.lists": "Listes", "navigation_bar.logout": "Déconnexion", "navigation_bar.mutes": "Comptes masqués", + "navigation_bar.opened_in_classic_interface": "Les messages, les comptes et les pages spécifiques sont ouvertes dans l’interface classique.", "navigation_bar.personal": "Personnel", "navigation_bar.pins": "Messages épinglés", "navigation_bar.preferences": "Préférences", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c07f95f564..95e6667b5d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3270,6 +3270,22 @@ $ui-header-height: 55px; border-color: $ui-highlight-color; } +.switch-to-advanced { + color: $classic-primary-color; + background-color: $classic-base-color; + padding: 15px; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 12px; + font-size: 13px; + line-height: 18px; + + .switch-to-advanced__toggle { + color: $ui-button-tertiary-color; + font-weight: bold; + } +} + .column-link { background: lighten($ui-base-color, 8%); color: $primary-text-color; From a50d3d877fd076b1d4b53a9cd083f5c38842b974 Mon Sep 17 00:00:00 2001 From: Tyler Deitz Date: Thu, 31 Aug 2023 04:46:27 -0700 Subject: [PATCH 053/182] Add avatar image to webfinger responses (#26558) --- app/serializers/webfinger_serializer.rb | 37 +++++++---- .../well_known/webfinger_controller_spec.rb | 64 +++++++++++++++++++ 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/app/serializers/webfinger_serializer.rb b/app/serializers/webfinger_serializer.rb index 3ca3441169..b67cd2771a 100644 --- a/app/serializers/webfinger_serializer.rb +++ b/app/serializers/webfinger_serializer.rb @@ -18,18 +18,31 @@ class WebfingerSerializer < ActiveModel::Serializer end def links - if object.instance_actor? - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) }, - { rel: 'self', type: 'application/activity+json', href: instance_actor_url }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] - else - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) }, - { rel: 'self', type: 'application/activity+json', href: account_url(object) }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] + [ + { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_page_href }, + { rel: 'self', type: 'application/activity+json', href: self_href }, + { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, + ].tap do |x| + x << { rel: 'http://webfinger.net/rel/avatar', type: object.avatar.content_type, href: full_asset_url(object.avatar_original_url) } if show_avatar? end end + + private + + def show_avatar? + media_present = object.avatar.present? && object.avatar.content_type.present? + + # Show avatar only if an instance shows profiles to logged out users + allowed_by_config = ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] != 'true' && !Rails.configuration.x.limited_federation_mode + + media_present && allowed_by_config + end + + def profile_page_href + object.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(object) + end + + def self_href + object.instance_actor? ? instance_actor_url : account_url(object) + end end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 8dc0f329b6..20770a7211 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' describe WellKnown::WebfingerController do + include RoutingHelper + render_views describe 'GET #show' do @@ -167,5 +169,67 @@ describe WellKnown::WebfingerController do expect(response).to have_http_status(400) end end + + context 'when an account has an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('attachment.jpg')) } + let(:resource) { alice.to_webfinger_s } + + it 'returns avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to_not be_nil + expect(avatar_link[:type]).to eq alice.avatar.content_type + expect(avatar_link[:href]).to eq full_asset_url(alice.avatar) + end + + context 'with limited federation mode' do + before do + allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + + context 'when enabling DISALLOW_UNAUTHENTICATED_API_ACCESS' do + around do |example| + ClimateControl.modify DISALLOW_UNAUTHENTICATED_API_ACCESS: 'true' do + example.run + end + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + context 'when an account does not have an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: nil) } + let(:resource) { alice.to_webfinger_s } + + before do + perform_show! + end + + it 'does not return avatar in response' do + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + private + + def get_avatar_link(json) + json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } end end From 8ad87eeb93eb075088e2d76a362dc9e7146cc714 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Thu, 31 Aug 2023 08:53:24 -0300 Subject: [PATCH 054/182] Fix `/api/v1/timelines/tag/:hashtag` allowing for unauthenticated access when public preview is disabled (#26237) --- .../api/v1/timelines/tag_controller.rb | 5 ++ .../api/v1/timelines/tag_controller_spec.rb | 66 ++++++++++++++----- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index 9cd7b99046..a79d65c124 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::TagController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action :load_tag after_action :insert_pagination_headers, unless: -> { @statuses.empty? } @@ -12,6 +13,10 @@ class Api::V1::Timelines::TagController < Api::BaseController private + def require_auth? + !Setting.timeline_preview + end + def load_tag @tag = Tag.find_normalized(params[:id]) end diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 7189110833..1c60798fcf 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -5,36 +5,66 @@ require 'rails_helper' describe Api::V1::Timelines::TagController do render_views - let(:user) { Fabricate(:user) } + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') } 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) } + describe 'GET #show' do + subject do + get :show, params: { id: 'test' } + end - describe 'GET #show' do - before do - PostStatusService.new.call(user.account, text: 'It is a #test') + before do + PostStatusService.new.call(user.account, text: 'It is a #test') + end + + context 'when the instance allows public preview' do + context 'when the user is not authenticated' do + let(:token) { nil } + + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end - it 'returns http success' do - get :show, params: { id: 'test' } - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) + context 'when the user is authenticated' do + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end end - end - context 'without a user context' do - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil) } + context 'when the instance does not allow public preview' do + before do + Form::AdminSettings.new(timeline_preview: false).save + end - describe 'GET #show' do - it 'returns http success' do - get :show, params: { id: 'test' } - expect(response).to have_http_status(200) - expect(response.headers['Link']).to be_nil + context 'when the user is not authenticated' do + let(:token) { nil } + + it 'returns http unauthorized' do + subject + + expect(response).to have_http_status(401) + end + end + + context 'when the user is authenticated' do + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end end end From 1b9fe4882f3513c0ad9bb049e336f45fffca2a85 Mon Sep 17 00:00:00 2001 From: Santiago Kozak Date: Thu, 31 Aug 2023 09:15:58 -0300 Subject: [PATCH 055/182] Allow filter form in profiles directory to wrap (#26682) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 95e6667b5d..f61cd059fe 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2381,6 +2381,7 @@ $ui-header-height: 55px; .filter-form { display: flex; + flex-wrap: wrap; } .autosuggest-textarea__textarea { From df241f222b18797fead1a0c5b5aa09cc3d0914ba Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 31 Aug 2023 14:59:50 +0200 Subject: [PATCH 056/182] Fix comment in build image workflow (#26740) --- .github/workflows/build-container-image.yml | 2 -- .github/workflows/build-releases.yml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 897bb9caaa..a1aeddf201 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -76,8 +76,6 @@ jobs: if: ${{ inputs.push_to_images != '' }} with: images: ${{ inputs.push_to_images }} - # Only tag with latest when ran against the latest stable branch - # This needs to be updated after each minor version release flavor: ${{ inputs.flavor }} tags: ${{ inputs.tags }} labels: ${{ inputs.labels }} diff --git a/.github/workflows/build-releases.yml b/.github/workflows/build-releases.yml index b408174688..f739a69d9a 100644 --- a/.github/workflows/build-releases.yml +++ b/.github/workflows/build-releases.yml @@ -17,6 +17,8 @@ jobs: push_to_images: | tootsuite/mastodon ghcr.io/mastodon/mastodon + # Only tag with latest when ran against the latest stable branch + # This needs to be updated after each minor version release flavor: | latest=${{ startsWith(github.ref, 'refs/tags/v4.1.') }} tags: | From 76033c02ff1f97337b3d93f53feb1b8e20b69a65 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 15:35:58 +0200 Subject: [PATCH 057/182] Fix searching by username by reverting account verbatim tokenizer to `standard` (#26739) --- app/chewy/accounts_index.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 8881b08f66..00db257ac7 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -34,7 +34,7 @@ class AccountsIndex < Chewy::Index }, verbatim: { - tokenizer: 'uax_url_email', + tokenizer: 'standard', filter: %w(lowercase asciifolding cjk_width), }, From 3f1b38932b41a0f24b7db21b54b11f01e7e46834 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 15:47:30 +0200 Subject: [PATCH 058/182] Fix `AddUniqueIndexOnPreviewCardsStatuses` migration requiring PostgreSQL 12+ in some cases (#26737) --- ...1_add_unique_index_on_preview_cards_statuses.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb b/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb index c35ad80028..3e9ab134b7 100644 --- a/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb +++ b/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb @@ -15,10 +15,22 @@ class AddUniqueIndexOnPreviewCardsStatuses < ActiveRecord::Migration[6.1] private + def supports_concurrent_reindex? + @supports_concurrent_reindex ||= begin + version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i + version >= 12_000 + end + end + def deduplicate_and_reindex! deduplicate_preview_cards! - safety_assured { execute 'REINDEX INDEX CONCURRENTLY preview_cards_statuses_pkey' } + if supports_concurrent_reindex? + safety_assured { execute 'REINDEX INDEX CONCURRENTLY preview_cards_statuses_pkey' } + else + remove_index :preview_cards_statuses, name: :preview_cards_statuses_pkey + add_index :preview_cards_statuses, [:status_id, :preview_card_id], name: :preview_cards_statuses_pkey, algorithm: :concurrently, unique: true + end rescue ActiveRecord::RecordNotUnique retry end From 703178c11778cc5b7ccb388eb76eda33ffa67955 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 31 Aug 2023 17:21:06 +0200 Subject: [PATCH 059/182] Fix videos not playing in some browsers due to unsupported color space (#26745) --- app/models/media_attachment.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 88abd93390..984f4252a1 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -100,6 +100,8 @@ class MediaAttachment < ApplicationRecord output: { 'loglevel' => 'fatal', 'preset' => 'veryfast', + 'movflags' => 'faststart', # Move metadata to start of file so playback can begin before download finishes + 'pix_fmt' => 'yuv420p', # Ensure color space for cross-browser compatibility 'c:v' => 'h264', 'c:a' => 'aac', 'b:a' => '192k', From dcd8d25ac1534fe224d1ff55ed4641ced94540f2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 19:04:27 +0200 Subject: [PATCH 060/182] Change importers to avoid a few inefficiencies (#26721) --- app/lib/importer/accounts_index_importer.rb | 6 ++-- app/lib/importer/base_importer.rb | 8 +++++ app/lib/importer/instances_index_importer.rb | 6 ++-- .../public_statuses_index_importer.rb | 6 ++-- app/lib/importer/statuses_index_importer.rb | 35 ++++++++----------- app/lib/importer/tags_index_importer.rb | 6 ++-- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/app/lib/importer/accounts_index_importer.rb b/app/lib/importer/accounts_index_importer.rb index fd869c3960..d8b9190275 100644 --- a/app/lib/importer/accounts_index_importer.rb +++ b/app/lib/importer/accounts_index_importer.rb @@ -4,10 +4,10 @@ class Importer::AccountsIndexImporter < Importer::BaseImporter def import! scope.includes(:account_stat).find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |accounts| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: accounts).bulk_body + bulk = build_bulk_body(accounts) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb index cc1b7b44d7..a21557d303 100644 --- a/app/lib/importer/base_importer.rb +++ b/app/lib/importer/base_importer.rb @@ -68,6 +68,14 @@ class Importer::BaseImporter protected + def build_bulk_body(to_import) + # Specialize `Chewy::Index::Import::BulkBuilder#bulk_body` to avoid a few + # inefficiencies, as none of our fields or join fields and we do not need + # `BulkBuilder`'s versatility. + crutches = Chewy::Index::Crutch::Crutches.new index, to_import + to_import.map { |object| { index: { _id: object.id, data: index.compose(object, crutches, fields: []) } } } + end + def in_work_unit(...) work_unit = Concurrent::Promises.future_on(@executor, ...) diff --git a/app/lib/importer/instances_index_importer.rb b/app/lib/importer/instances_index_importer.rb index 7318b51b5d..ebdceb72ed 100644 --- a/app/lib/importer/instances_index_importer.rb +++ b/app/lib/importer/instances_index_importer.rb @@ -4,10 +4,10 @@ class Importer::InstancesIndexImporter < Importer::BaseImporter def import! index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |instances| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: instances).bulk_body + bulk = build_bulk_body(instances) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb index 72d02318b1..ebaac3794f 100644 --- a/app/lib/importer/public_statuses_index_importer.rb +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -5,11 +5,11 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter scope.select(:id).find_in_batches(batch_size: @batch_size) do |batch| in_work_unit(batch.pluck(:id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body + build_bulk_body(index.adapter.default_scope.where(id: status_ids)) end - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index 285ddc871a..08ad3e3797 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -13,32 +13,25 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| - bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: index.adapter.default_scope.where(id: status_ids)).bulk_body - end - - indexed = 0 deleted = 0 - # We can't use the delete_if proc to do the filtering because delete_if - # is called before rendering the data and we need to filter based - # on the results of the filter, so this filtering happens here instead - bulk.map! do |entry| - new_entry = if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank? - { delete: entry[:index].except(:data) } - else - entry - end - - if new_entry[:index] - indexed += 1 - else - deleted += 1 + bulk = ActiveRecord::Base.connection_pool.with_connection do + to_index = index.adapter.default_scope.where(id: status_ids) + crutches = Chewy::Index::Crutch::Crutches.new index, to_index + to_index.map do |object| + # This is unlikely to happen, but the post may have been + # un-interacted with since it was queued for indexing + if object.searchable_by.empty? + deleted += 1 + { delete: { _id: object.id } } + else + { index: { _id: object.id, data: index.compose(object, crutches, fields: []) } } + end end - - new_entry end + indexed = bulk.size - deleted + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) [indexed, deleted] diff --git a/app/lib/importer/tags_index_importer.rb b/app/lib/importer/tags_index_importer.rb index 77710ed7de..067fd8cd2d 100644 --- a/app/lib/importer/tags_index_importer.rb +++ b/app/lib/importer/tags_index_importer.rb @@ -4,10 +4,10 @@ class Importer::TagsIndexImporter < Importer::BaseImporter def import! index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |tags| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: tags).bulk_body + bulk = build_bulk_body(tags) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) From a189771256e09dd2ae437fdcfd120640f05b775f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 31 Aug 2023 19:04:44 +0200 Subject: [PATCH 061/182] Fix search queries with slash causing or-condition (#26699) --- app/lib/search_query_transformer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 86e3f50005..f10ccfb283 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -25,7 +25,7 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_query(clause) case clause when TermClause - { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'] } } + { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'], operator: 'and' } } when PhraseClause { match_phrase: { text: { query: clause.phrase } } } else From 340e86a137dcde9941e1f50d4904853fa3886611 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Sep 2023 09:27:03 +0200 Subject: [PATCH 062/182] Fix not being able to invoke phrase search using unicode quotation marks (#26687) --- app/services/search_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 40d82fc525..9a40d7bdd5 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class SearchService < BaseService + QUOTE_EQUIVALENT_CHARACTERS = /[“”„«»「」『』《》]/ + def call(query, account, limit, options = {}) - @query = query&.strip + @query = query&.strip&.gsub(QUOTE_EQUIVALENT_CHARACTERS, '"') @account = account @options = options @limit = limit.to_i From 1f57ff8dd18395b72099df41db61bd6143fce89f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Sep 2023 09:43:12 +0200 Subject: [PATCH 063/182] Fix unmatched quotes and prefixes causing search to fail (#26701) --- app/lib/search_query_parser.rb | 4 +- app/lib/search_query_transformer.rb | 101 +++++++++++----------- spec/lib/search_query_parser_spec.rb | 98 +++++++++++++++++++++ spec/lib/search_query_transformer_spec.rb | 57 ++++++++++-- 4 files changed, 200 insertions(+), 60 deletions(-) create mode 100644 spec/lib/search_query_parser_spec.rb diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 5d6ffbf29d..1c57b9b024 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -6,10 +6,10 @@ class SearchQueryParser < Parslet::Parser rule(:colon) { str(':') } rule(:space) { match('\s').repeat(1) } rule(:operator) { (str('+') | str('-')).as(:operator) } - rule(:prefix) { (term >> colon).as(:prefix) } + rule(:prefix) { term >> colon } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } - rule(:clause) { (operator.maybe >> prefix.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index f10ccfb283..e81c0c081e 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -1,50 +1,32 @@ # frozen_string_literal: true class SearchQueryTransformer < Parslet::Transform + SUPPORTED_PREFIXES = %w( + has + is + language + from + before + after + during + ).freeze + class Query - attr_reader :should_clauses, :must_not_clauses, :must_clauses, :filter_clauses + attr_reader :must_not_clauses, :must_clauses, :filter_clauses def initialize(clauses) - grouped = clauses.chunk(&:operator).to_h - @should_clauses = grouped.fetch(:should, []) + grouped = clauses.compact.chunk(&:operator).to_h @must_not_clauses = grouped.fetch(:must_not, []) @must_clauses = grouped.fetch(:must, []) @filter_clauses = grouped.fetch(:filter, []) end def apply(search) - should_clauses.each { |clause| search = search.query.should(clause_to_query(clause)) } - must_clauses.each { |clause| search = search.query.must(clause_to_query(clause)) } - must_not_clauses.each { |clause| search = search.query.must_not(clause_to_query(clause)) } - filter_clauses.each { |clause| search = search.filter(**clause_to_filter(clause)) } + must_clauses.each { |clause| search = search.query.must(clause.to_query) } + must_not_clauses.each { |clause| search = search.query.must_not(clause.to_query) } + filter_clauses.each { |clause| search = search.filter(**clause.to_query) } search.query.minimum_should_match(1) end - - private - - def clause_to_query(clause) - case clause - when TermClause - { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'], operator: 'and' } } - when PhraseClause - { match_phrase: { text: { query: clause.phrase } } } - else - raise "Unexpected clause type: #{clause}" - end - end - - def clause_to_filter(clause) - case clause - when PrefixClause - if clause.negated? - { bool: { must_not: { clause.type => { clause.filter => clause.term } } } } - else - { clause.type => { clause.filter => clause.term } } - end - else - raise "Unexpected clause type: #{clause}" - end - end end class Operator @@ -63,31 +45,38 @@ class SearchQueryTransformer < Parslet::Transform end class TermClause - attr_reader :prefix, :operator, :term + attr_reader :operator, :term - def initialize(prefix, operator, term) - @prefix = prefix + def initialize(operator, term) @operator = Operator.symbol(operator) @term = term end + + def to_query + { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } + end end class PhraseClause - attr_reader :prefix, :operator, :phrase + attr_reader :operator, :phrase - def initialize(prefix, operator, phrase) - @prefix = prefix + def initialize(operator, phrase) @operator = Operator.symbol(operator) @phrase = phrase end + + def to_query + { match_phrase: { text: { query: @phrase } } } + end end class PrefixClause - attr_reader :type, :filter, :operator, :term + attr_reader :operator, :prefix, :term def initialize(prefix, operator, term, options = {}) - @negated = operator == '-' - @options = options + @prefix = prefix + @negated = operator == '-' + @options = options @operator = :filter case prefix @@ -116,12 +105,16 @@ class SearchQueryTransformer < Parslet::Transform @type = :range @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } else - raise Mastodon::SyntaxError + raise "Unknown prefix: #{prefix}" end end - def negated? - @negated + def to_query + if @negated + { bool: { must_not: { @type => { @filter => @term } } } } + else + { @type => { @filter => @term } } + end end private @@ -159,18 +152,26 @@ class SearchQueryTransformer < Parslet::Transform prefix = clause[:prefix][:term].to_s if clause[:prefix] operator = clause[:operator]&.to_s - if clause[:prefix] + if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix) PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) + elsif clause[:prefix] + TermClause.new(operator, "#{prefix} #{clause[:term]}") elsif clause[:term] - TermClause.new(prefix, operator, clause[:term].to_s) + TermClause.new(operator, clause[:term].to_s) elsif clause[:shortcode] - TermClause.new(prefix, operator, ":#{clause[:term]}:") + TermClause.new(operator, ":#{clause[:term]}:") elsif clause[:phrase] - PhraseClause.new(prefix, operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) + PhraseClause.new(operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) else raise "Unexpected clause type: #{clause}" end end - rule(query: sequence(:clauses)) { Query.new(clauses) } + rule(junk: subtree(:junk)) do + nil + end + + rule(query: sequence(:clauses)) do + Query.new(clauses) + end end diff --git a/spec/lib/search_query_parser_spec.rb b/spec/lib/search_query_parser_spec.rb new file mode 100644 index 0000000000..66b0e8f9e2 --- /dev/null +++ b/spec/lib/search_query_parser_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'parslet/rig/rspec' + +describe SearchQueryParser do + let(:parser) { described_class.new } + + context 'with term' do + it 'consumes "hello"' do + expect(parser.term).to parse('hello') + end + end + + context 'with prefix' do + it 'consumes "foo:"' do + expect(parser.prefix).to parse('foo:') + end + end + + context 'with operator' do + it 'consumes "+"' do + expect(parser.operator).to parse('+') + end + + it 'consumes "-"' do + expect(parser.operator).to parse('-') + end + end + + context 'with shortcode' do + it 'consumes ":foo:"' do + expect(parser.shortcode).to parse(':foo:') + end + end + + context 'with phrase' do + it 'consumes "hello world"' do + expect(parser.phrase).to parse('"hello world"') + end + end + + context 'with clause' do + it 'consumes "foo"' do + expect(parser.clause).to parse('foo') + end + + it 'consumes "-foo"' do + expect(parser.clause).to parse('-foo') + end + + it 'consumes "foo:bar"' do + expect(parser.clause).to parse('foo:bar') + end + + it 'consumes "-foo:bar"' do + expect(parser.clause).to parse('-foo:bar') + end + + it 'consumes \'foo:"hello world"\'' do + expect(parser.clause).to parse('foo:"hello world"') + end + + it 'consumes \'-foo:"hello world"\'' do + expect(parser.clause).to parse('-foo:"hello world"') + end + + it 'consumes "foo:"' do + expect(parser.clause).to parse('foo:') + end + + it 'consumes \'"\'' do + expect(parser.clause).to parse('"') + end + end + + context 'with query' do + it 'consumes "hello -world"' do + expect(parser.query).to parse('hello -world') + end + + it 'consumes \'foo "hello world"\'' do + expect(parser.query).to parse('foo "hello world"') + end + + it 'consumes "foo:bar hello"' do + expect(parser.query).to parse('foo:bar hello') + end + + it 'consumes \'"hello" world "\'' do + expect(parser.query).to parse('"hello" world "') + end + + it 'consumes "foo:bar bar: hello"' do + expect(parser.query).to parse('foo:bar bar: hello') + end + end +end diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 953f9acb2f..17f06d2833 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -3,16 +3,57 @@ require 'rails_helper' describe SearchQueryTransformer do - describe 'initialization' do - let(:parser) { SearchQueryParser.new.parse('query') } + subject { described_class.new.apply(parser, current_account: nil) } - it 'sets attributes' do - transformer = described_class.new.apply(parser) + let(:parser) { SearchQueryParser.new.parse(query) } - expect(transformer.should_clauses.first).to be_nil - expect(transformer.must_clauses.first).to be_a(SearchQueryTransformer::TermClause) - expect(transformer.must_not_clauses.first).to be_nil - expect(transformer.filter_clauses.first).to be_nil + context 'with "hello world"' do + let(:query) { 'hello world' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello world) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "hello -world"' do + let(:query) { 'hello -world' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello) + expect(subject.must_not_clauses.map(&:term)).to match_array %w(world) + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "hello is:reply"' do + let(:query) { 'hello is:reply' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses.map(&:term)).to match_array %w(reply) + end + end + + context 'with "foo: bar"' do + let(:query) { 'foo: bar' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(foo bar) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "foo:bar"' do + let(:query) { 'foo:bar' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to contain_exactly('foo bar') + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty end end end From 50423eec752fb59a84be6ad8e7713f97198eb96c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:17:08 +0200 Subject: [PATCH 064/182] Update dependency oj to v3.16.1 (#26749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f1a61c5e09..6dd8c567f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -482,7 +482,7 @@ GEM nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oj (3.16.0) + oj (3.16.1) omniauth (2.1.1) hashie (>= 3.4.6) rack (>= 2.2.3) From 8e34673487ddb4044e81e3e01ffe5214461b9044 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:48:49 +0200 Subject: [PATCH 065/182] Update dependency pg to v1.5.4 (#26750) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6dd8c567f8..4e30c42222 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -519,7 +519,7 @@ GEM parslet (2.0.0) pastel (0.8.0) tty-color (~> 0.5) - pg (1.5.3) + pg (1.5.4) pghero (3.3.3) activerecord (>= 6) posix-spawn (0.3.15) From 5cf3e206bb1ef2f098e4662b5fcd2db660ef10fd Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:09:44 +0200 Subject: [PATCH 066/182] Revert to using primary database in IndexingScheduler (#26754) --- app/workers/scheduler/indexing_scheduler.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 1b09730c7d..ff1b744442 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -16,9 +16,7 @@ class Scheduler::IndexingScheduler indexes.each do |type| with_redis do |redis| redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE).each_slice(IMPORT_BATCH_SIZE) do |ids| - with_read_replica do - type.import!(ids) - end + type.import!(ids) redis.srem("chewy:queue:#{type.name}", ids) end From 20913b52bb192a9525b3da53a1d25c816a4a177d Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:13:27 +0200 Subject: [PATCH 067/182] Fix search popout including full-text search instructions when full-text search is disabled (#26755) --- .../features/compose/components/search.jsx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx index 1c629bcbb4..848b812632 100644 --- a/app/javascript/mastodon/features/compose/components/search.jsx +++ b/app/javascript/mastodon/features/compose/components/search.jsx @@ -80,7 +80,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions().concat(this.defaultOptions); + const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions(); switch(e.key) { case 'Escape': @@ -353,15 +353,19 @@ class Search extends PureComponent { )} -

+ {searchEnabled && ( + <> +

-
- {this.defaultOptions.map(({ key, label, action }, i) => ( - - ))} -
+
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
+ + )}
); From 7d3930a52059f023cc88d76abd95208a74ca6ccd Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:41:10 +0200 Subject: [PATCH 068/182] Add `authorized_fetch` server setting in addition to env var (#25798) --- app/controllers/application_controller.rb | 5 +---- app/helpers/authorized_fetch_helper.rb | 11 +++++++++++ app/javascript/styles/mastodon/accounts.scss | 2 ++ app/javascript/styles/mastodon/forms.scss | 1 + app/models/form/admin_settings.rb | 10 ++++++++++ app/services/concerns/payloadable.rb | 4 +++- app/views/admin/settings/discovery/show.html.haml | 5 +++++ config/i18n-tasks.yml | 2 +- config/initializers/simple_form.rb | 5 +++-- config/locales/en.yml | 5 +++++ config/locales/simple_form.en.yml | 1 + 11 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 app/helpers/authorized_fetch_helper.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 975315e247..6ec93f824e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base include CacheConcern include DomainControlHelper include DatabaseHelper + include AuthorizedFetchHelper helper_method :current_account helper_method :current_session @@ -51,10 +52,6 @@ class ApplicationController < ActionController::Base private - def authorized_fetch_mode? - ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.limited_federation_mode - end - def public_fetch_mode? !authorized_fetch_mode? end diff --git a/app/helpers/authorized_fetch_helper.rb b/app/helpers/authorized_fetch_helper.rb new file mode 100644 index 0000000000..ce87526e6a --- /dev/null +++ b/app/helpers/authorized_fetch_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module AuthorizedFetchHelper + def authorized_fetch_mode? + ENV.fetch('AUTHORIZED_FETCH') { Setting.authorized_fetch } == 'true' || Rails.configuration.x.limited_federation_mode + end + + def authorized_fetch_overridden? + ENV.key?('AUTHORIZED_FETCH') || Rails.configuration.x.limited_federation_mode + end +end diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index 2a5285ee02..80d6c13cef 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -188,6 +188,7 @@ } .information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended { display: inline-block; @@ -204,6 +205,7 @@ } .information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended { background-color: rgba($ui-secondary-color, 0.1); diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index a7079c1457..0f8eecee01 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -103,6 +103,7 @@ code { } } + .overridden, .recommended, .not_recommended { position: absolute; diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index a6be55fd7b..7be026d85f 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -3,6 +3,8 @@ class Form::AdminSettings include ActiveModel::Model + include AuthorizedFetchHelper + KEYS = %i( site_contact_username site_contact_email @@ -34,6 +36,7 @@ class Form::AdminSettings backups_retention_period status_page_url captcha_enabled + authorized_fetch ).freeze INTEGER_KEYS = %i( @@ -54,6 +57,7 @@ class Form::AdminSettings noindex require_invite_text captcha_enabled + authorized_fetch ).freeze UPLOAD_KEYS = %i( @@ -61,6 +65,10 @@ class Form::AdminSettings mascot ).freeze + OVERRIDEN_SETTINGS = { + authorized_fetch: :authorized_fetch_mode?, + }.freeze + attr_accessor(*KEYS) validates :registrations_mode, inclusion: { in: %w(open approved none) }, if: -> { defined?(@registrations_mode) } @@ -80,6 +88,8 @@ class Form::AdminSettings stored_value = if UPLOAD_KEYS.include?(key) SiteUpload.where(var: key).first_or_initialize(var: key) + elsif OVERRIDEN_SETTINGS.include?(key) + public_send(OVERRIDEN_SETTINGS[key]) else Setting.public_send(key) end diff --git a/app/services/concerns/payloadable.rb b/app/services/concerns/payloadable.rb index 1389a42ed6..bd9d9d74b5 100644 --- a/app/services/concerns/payloadable.rb +++ b/app/services/concerns/payloadable.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Payloadable + include AuthorizedFetchHelper + # @param [ActiveModelSerializers::Model] record # @param [ActiveModelSerializers::Serializer] serializer # @param [Hash] options @@ -23,6 +25,6 @@ module Payloadable end def signing_enabled? - ENV['AUTHORIZED_FETCH'] != 'true' && !Rails.configuration.x.limited_federation_mode + !authorized_fetch_mode? end end diff --git a/app/views/admin/settings/discovery/show.html.haml b/app/views/admin/settings/discovery/show.html.haml index c48e0fdc62..62011d5c56 100644 --- a/app/views/admin/settings/discovery/show.html.haml +++ b/app/views/admin/settings/discovery/show.html.haml @@ -39,6 +39,11 @@ .fields-group = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, recommended: :recommended + %h4= t('admin.settings.security.federation_authentication') + + .fields-group + = f.input :authorized_fetch, as: :boolean, wrapper: :with_label, label: t('admin.settings.security.authorized_fetch'), warning_hint: authorized_fetch_overridden? ? t('admin.settings.security.authorized_fetch_overridden_hint') : nil, hint: t('admin.settings.security.authorized_fetch_hint'), disabled: authorized_fetch_overridden?, recommended: authorized_fetch_overridden? ? :overridden : nil + %h4= t('admin.settings.discovery.follow_recommendations') .fields-group diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index d0677b80fb..2d4487ce56 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -50,7 +50,7 @@ ignore_unused: - 'activerecord.errors.*' - '{devise,pagination,doorkeeper}.*' - '{date,datetime,time,number}.*' - - 'simple_form.{yes,no,recommended,not_recommended}' + - 'simple_form.{yes,no,recommended,not_recommended,overridden}' - 'simple_form.{placeholders,hints,labels}.*' - 'simple_form.{error_notification,required}.:' - 'errors.messages.*' diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 9d90cc6ca8..a7a2d251e5 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -97,7 +97,8 @@ SimpleForm.setup do |config| end end - b.use :hint, wrap_with: { tag: :span, class: :hint } + b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] } + b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :error, wrap_with: { tag: :span, class: :error } end @@ -111,8 +112,8 @@ SimpleForm.setup do |config| config.wrappers :with_block_label, class: [:input, :with_block_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b| b.use :html5 b.use :label - b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] } + b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :input, wrap_with: { tag: :div, class: :label_input } b.use :error, wrap_with: { tag: :span, class: :error } end diff --git a/config/locales/en.yml b/config/locales/en.yml index 8bdfd1ec91..693155d6ef 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -770,6 +770,11 @@ en: approved: Approval required for sign up none: Nobody can sign up open: Anyone can sign up + security: + authorized_fetch: Require authentication from federated servers + authorized_fetch_hint: Requiring authentication from federated servers enables stricter enforcement of both user-level and server-level blocks. However, this comes at the cost of a performance penalty, reduces the reach of your replies, and may introduce compatibility issues with some federated services. In addition, this will not prevent dedicated actors from fetching your public posts and accounts. + authorized_fetch_overridden_hint: You are currently unable to change this setting because it is overridden by an environment variable. + federation_authentication: Federation authentication enforcement title: Server settings site_uploads: delete: Delete uploaded file diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index efda7b778b..b1297606bc 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -317,6 +317,7 @@ en: url: Endpoint URL 'no': 'No' not_recommended: Not recommended + overridden: Overridden recommended: Recommended required: mark: "*" From 8bffce5f015ace5b73d5a8aa746c02e374d927bb Mon Sep 17 00:00:00 2001 From: Gabriel Simmer Date: Fri, 1 Sep 2023 16:44:28 +0100 Subject: [PATCH 069/182] Move to ioredis for streaming (#26581) Co-authored-by: Emelia Smith --- package.json | 2 +- streaming/index.js | 82 +++++++++++++++++------------------- yarn.lock | 101 +++++++++++++++++++++------------------------ 3 files changed, 85 insertions(+), 100 deletions(-) diff --git a/package.json b/package.json index 4038831050..e92115610b 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "immutable": "^4.3.0", "imports-loader": "^1.2.0", "intl-messageformat": "^10.3.5", + "ioredis": "^5.3.2", "js-yaml": "^4.1.0", "jsdom": "^22.1.0", "lodash": "^4.17.21", @@ -118,7 +119,6 @@ "react-swipeable-views": "^0.14.0", "react-textarea-autosize": "^8.4.1", "react-toggle": "^4.1.3", - "redis": "^4.6.5", "redux": "^4.2.1", "redux-immutable": "^4.0.0", "redux-thunk": "^2.4.2", diff --git a/streaming/index.js b/streaming/index.js index a241fa3280..c9fac063df 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -6,12 +6,12 @@ const url = require('url'); const dotenv = require('dotenv'); const express = require('express'); +const Redis = require('ioredis'); const { JSDOM } = require('jsdom'); const log = require('npmlog'); const pg = require('pg'); const dbUrlToConfig = require('pg-connection-string').parse; const metrics = require('prom-client'); -const redis = require('redis'); const uuid = require('uuid'); const WebSocket = require('ws'); @@ -24,30 +24,12 @@ dotenv.config({ log.level = process.env.LOG_LEVEL || 'verbose'; /** - * @param {Object.} defaultConfig - * @param {string} redisUrl + * @param {Object.} config */ -const redisUrlToClient = async (defaultConfig, redisUrl) => { - const config = defaultConfig; - - let client; - - if (!redisUrl) { - client = redis.createClient(config); - } else if (redisUrl.startsWith('unix://')) { - client = redis.createClient(Object.assign(config, { - socket: { - path: redisUrl.slice(7), - }, - })); - } else { - client = redis.createClient(Object.assign(config, { - url: redisUrl, - })); - } - +const createRedisClient = async (config) => { + const { redisParams, redisUrl } = config; + const client = new Redis(redisUrl, redisParams); client.on('error', (err) => log.error('Redis Client Error!', err)); - await client.connect(); return client; }; @@ -147,23 +129,22 @@ const pgConfigFromEnv = (env) => { * @returns {Object.} configuration for the Redis connection */ const redisConfigFromEnv = (env) => { - const redisNamespace = env.REDIS_NAMESPACE || null; + // ioredis *can* transparently add prefixes for us, but it doesn't *in some cases*, + // which means we can't use it. But this is something that should be looked into. + const redisPrefix = env.REDIS_NAMESPACE ? `${env.REDIS_NAMESPACE}:` : ''; const redisParams = { - socket: { - host: env.REDIS_HOST || '127.0.0.1', - port: env.REDIS_PORT || 6379, - }, - database: env.REDIS_DB || 0, + host: env.REDIS_HOST || '127.0.0.1', + port: env.REDIS_PORT || 6379, + db: env.REDIS_DB || 0, password: env.REDIS_PASSWORD || undefined, }; - if (redisNamespace) { - redisParams.namespace = redisNamespace; + // redisParams.path takes precedence over host and port. + if (env.REDIS_URL && env.REDIS_URL.startsWith('unix://')) { + redisParams.path = env.REDIS_URL.slice(7); } - const redisPrefix = redisNamespace ? `${redisNamespace}:` : ''; - return { redisParams, redisPrefix, @@ -179,15 +160,15 @@ const startServer = async () => { const pgPool = new pg.Pool(pgConfigFromEnv(process.env)); const server = http.createServer(app); - const { redisParams, redisUrl, redisPrefix } = redisConfigFromEnv(process.env); - /** * @type {Object.): void>>} */ const subs = {}; - const redisSubscribeClient = await redisUrlToClient(redisParams, redisUrl); - const redisClient = await redisUrlToClient(redisParams, redisUrl); + const redisConfig = redisConfigFromEnv(process.env); + const redisSubscribeClient = await createRedisClient(redisConfig); + const redisClient = await createRedisClient(redisConfig); + const { redisPrefix } = redisConfig; // Collect metrics from Node.js metrics.collectDefaultMetrics(); @@ -277,13 +258,13 @@ const startServer = async () => { }; /** - * @param {string} message * @param {string} channel + * @param {string} message */ - const onRedisMessage = (message, channel) => { + const onRedisMessage = (channel, message) => { const callbacks = subs[channel]; - log.silly(`New message on channel ${channel}`); + log.silly(`New message on channel ${redisPrefix}${channel}`); if (!callbacks) { return; @@ -294,6 +275,7 @@ const startServer = async () => { callbacks.forEach(callback => callback(json)); }; + redisSubscribeClient.on("message", onRedisMessage); /** * @callback SubscriptionListener @@ -312,8 +294,14 @@ const startServer = async () => { if (subs[channel].length === 0) { log.verbose(`Subscribe ${channel}`); - redisSubscribeClient.subscribe(channel, onRedisMessage); - redisSubscriptions.inc(); + redisSubscribeClient.subscribe(channel, (err, count) => { + if (err) { + log.error(`Error subscribing to ${channel}`); + } + else { + redisSubscriptions.set(count); + } + }); } subs[channel].push(callback); @@ -334,8 +322,14 @@ const startServer = async () => { if (subs[channel].length === 0) { log.verbose(`Unsubscribe ${channel}`); - redisSubscribeClient.unsubscribe(channel); - redisSubscriptions.dec(); + redisSubscribeClient.unsubscribe(channel, (err, count) => { + if (err) { + log.error(`Error unsubscribing to ${channel}`); + } + else { + redisSubscriptions.set(count); + } + }); delete subs[channel]; } }; diff --git a/yarn.lock b/yarn.lock index 880ded9773..74ed5214e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1452,6 +1452,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1786,40 +1791,6 @@ resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.7.tgz#54af8d66160a8a7bf7d8f184703d2bf4b3fab914" integrity sha512-J2v5Ca7HgejO7diGKiDylaVDQKmbQ5FJih6Oo3hXuBKEuXlcaccJu64lj8MNVLaPVyZx0g4gaOQZQz95QEb/hg== -"@redis/bloom@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" - integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== - -"@redis/client@1.5.9": - version "1.5.9" - resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.9.tgz#c4ee81bbfedb4f1d9c7c5e9859661b9388fb4021" - integrity sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ== - dependencies: - cluster-key-slot "1.1.2" - generic-pool "3.9.0" - yallist "4.0.0" - -"@redis/graph@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519" - integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg== - -"@redis/json@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1" - integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw== - -"@redis/search@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.3.tgz#b5a6837522ce9028267fe6f50762a8bcfd2e998b" - integrity sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng== - -"@redis/time-series@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.5.tgz#a6d70ef7a0e71e083ea09b967df0a0ed742bc6ad" - integrity sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg== - "@reduxjs/toolkit@^1.9.5": version "1.9.5" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.5.tgz#d3987849c24189ca483baa7aa59386c8e52077c4" @@ -4111,7 +4082,7 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -cluster-key-slot@1.1.2: +cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== @@ -4857,6 +4828,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -6139,11 +6115,6 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -generic-pool@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" - integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6823,6 +6794,21 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ioredis@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.3.2.tgz#9139f596f62fc9c72d873353ac5395bcf05709f7" + integrity sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -10283,17 +10269,17 @@ redent@^4.0.0: indent-string "^5.0.0" strip-indent "^4.0.0" -redis@^4.6.5: - version "4.6.8" - resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.8.tgz#54c5992e8a5ba512506fe9f53142cadc405547e7" - integrity sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ== +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== dependencies: - "@redis/bloom" "1.2.0" - "@redis/client" "1.5.9" - "@redis/graph" "1.1.0" - "@redis/json" "1.0.4" - "@redis/search" "1.1.3" - "@redis/time-series" "1.0.5" + redis-errors "^1.0.0" redux-immutable@^4.0.0: version "4.0.0" @@ -11211,6 +11197,11 @@ stacktrace-js@^2.0.2: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -12966,16 +12957,16 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@4.0.0, yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" From 97238e3496a87a232fba195be5805f29169513a4 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 17:47:07 +0200 Subject: [PATCH 070/182] Add admin notifications for new Mastodon versions (#26582) --- .../admin/software_updates_controller.rb | 18 ++ .../components/critical_update_banner.tsx | 26 +++ .../mastodon/features/home_timeline/index.jsx | 14 +- app/javascript/mastodon/initial_state.js | 2 + app/javascript/mastodon/locales/en.json | 3 + app/javascript/styles/mastodon/admin.scss | 5 + .../styles/mastodon/components.scss | 18 +- app/javascript/styles/mastodon/tables.scss | 5 + app/lib/admin/system_check.rb | 1 + .../system_check/software_version_check.rb | 27 +++ app/mailers/admin_mailer.rb | 16 ++ app/models/software_update.rb | 40 +++++ app/models/user_settings.rb | 1 + app/policies/software_update_policy.rb | 7 + app/presenters/initial_state_presenter.rb | 6 +- app/serializers/initial_state_serializer.rb | 2 + app/services/software_update_check_service.rb | 82 +++++++++ .../admin/software_updates/index.html.haml | 29 ++++ .../new_critical_software_updates.text.erb | 5 + .../new_software_updates.text.erb | 5 + .../preferences/notifications/show.html.haml | 6 +- .../software_update_check_scheduler.rb | 11 ++ config/locales/en.yml | 25 +++ config/locales/simple_form.en.yml | 6 + config/navigation.rb | 3 + config/routes/admin.rb | 2 + config/sidekiq.yml | 4 + .../20230822081029_create_software_updates.rb | 16 ++ db/schema.rb | 12 +- lib/mastodon/version.rb | 4 + lib/tasks/mastodon.rake | 4 + .../fabricators/software_update_fabricator.rb | 7 + spec/features/admin/software_updates_spec.rb | 23 +++ .../software_version_check_spec.rb | 133 +++++++++++++++ spec/mailers/admin_mailer_spec.rb | 42 +++++ spec/models/software_update_spec.rb | 87 ++++++++++ spec/policies/software_update_policy_spec.rb | 25 +++ .../software_update_check_service_spec.rb | 158 ++++++++++++++++++ .../software_update_check_scheduler_spec.rb | 20 +++ 39 files changed, 892 insertions(+), 8 deletions(-) create mode 100644 app/controllers/admin/software_updates_controller.rb create mode 100644 app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx create mode 100644 app/lib/admin/system_check/software_version_check.rb create mode 100644 app/models/software_update.rb create mode 100644 app/policies/software_update_policy.rb create mode 100644 app/services/software_update_check_service.rb create mode 100644 app/views/admin/software_updates/index.html.haml create mode 100644 app/views/admin_mailer/new_critical_software_updates.text.erb create mode 100644 app/views/admin_mailer/new_software_updates.text.erb create mode 100644 app/workers/scheduler/software_update_check_scheduler.rb create mode 100644 db/migrate/20230822081029_create_software_updates.rb create mode 100644 spec/fabricators/software_update_fabricator.rb create mode 100644 spec/features/admin/software_updates_spec.rb create mode 100644 spec/lib/admin/system_check/software_version_check_spec.rb create mode 100644 spec/models/software_update_spec.rb create mode 100644 spec/policies/software_update_policy_spec.rb create mode 100644 spec/services/software_update_check_service_spec.rb create mode 100644 spec/workers/scheduler/software_update_check_scheduler_spec.rb diff --git a/app/controllers/admin/software_updates_controller.rb b/app/controllers/admin/software_updates_controller.rb new file mode 100644 index 0000000000..52d8cb41e6 --- /dev/null +++ b/app/controllers/admin/software_updates_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class SoftwareUpdatesController < BaseController + before_action :check_enabled! + + def index + authorize :software_update, :index? + @software_updates = SoftwareUpdate.all.sort_by(&:gem_version) + end + + private + + def check_enabled! + not_found unless SoftwareUpdate.check_enabled? + end + end +end diff --git a/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx b/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx new file mode 100644 index 0000000000..d0dd2b6acd --- /dev/null +++ b/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx @@ -0,0 +1,26 @@ +import { FormattedMessage } from 'react-intl'; + +export const CriticalUpdateBanner = () => ( +
+
+

+ +

+

+ {' '} + + + +

+
+
+); diff --git a/app/javascript/mastodon/features/home_timeline/index.jsx b/app/javascript/mastodon/features/home_timeline/index.jsx index 1cd6edd7aa..8ff0377946 100644 --- a/app/javascript/mastodon/features/home_timeline/index.jsx +++ b/app/javascript/mastodon/features/home_timeline/index.jsx @@ -14,7 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container'; -import { me } from 'mastodon/initial_state'; +import { me, criticalUpdatesPending } from 'mastodon/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { expandHomeTimeline } from '../../actions/timelines'; @@ -23,6 +23,7 @@ import ColumnHeader from '../../components/column_header'; import StatusListContainer from '../ui/containers/status_list_container'; import { ColumnSettings } from './components/column_settings'; +import { CriticalUpdateBanner } from './components/critical_update_banner'; import { ExplorePrompt } from './components/explore_prompt'; const messages = defineMessages({ @@ -156,8 +157,9 @@ class HomeTimeline extends PureComponent { const { intl, hasUnread, columnId, multiColumn, tooSlow, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; const { signedIn } = this.context.identity; + const banners = []; - let announcementsButton, banner; + let announcementsButton; if (hasAnnouncements) { announcementsButton = ( @@ -173,8 +175,12 @@ class HomeTimeline extends PureComponent { ); } + if (criticalUpdatesPending) { + banners.push(); + } + if (tooSlow) { - banner = ; + banners.push(); } return ( @@ -196,7 +202,7 @@ class HomeTimeline extends PureComponent { {signedIn ? ( } accounts * @property {InitialStateLanguage[]} languages + * @property {boolean=} critical_updates_pending * @property {InitialStateMeta} meta */ @@ -140,6 +141,7 @@ export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); export const languages = initialState?.languages; +export const criticalUpdatesPending = initialState?.critical_updates_pending; // @ts-expect-error export const statusPageUrl = getMeta('status_page_url'); export const sso_redirect = getMeta('sso_redirect'); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 90bb9616f0..13cddba723 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -310,6 +310,9 @@ "home.explore_prompt.body": "Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. If that feels too quiet, you may want to:", "home.explore_prompt.title": "This is your home base within Mastodon.", "home.hide_announcements": "Hide announcements", + "home.pending_critical_update.body": "Please update your Mastodon server as soon as possible!", + "home.pending_critical_update.link": "See updates", + "home.pending_critical_update.title": "Critical security update available!", "home.show_announcements": "Show announcements", "interaction_modal.description.favourite": "With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.", "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.", diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index bbb6ffdff7..a65f35e7b1 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -143,6 +143,11 @@ $content-width: 840px; } } + .warning a { + color: $gold-star; + font-weight: 700; + } + .simple-navigation-active-leaf a { color: $primary-text-color; background-color: $ui-highlight-color; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f61cd059fe..10083a2a32 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -8860,7 +8860,8 @@ noscript { } } -.dismissable-banner { +.dismissable-banner, +.warning-banner { position: relative; margin: 10px; margin-bottom: 5px; @@ -8938,6 +8939,21 @@ noscript { } } +.warning-banner { + border: 1px solid $warning-red; + background: rgba($warning-red, 0.15); + + &__message { + h1 { + color: $warning-red; + } + + a { + color: $primary-text-color; + } + } +} + .image { position: relative; overflow: hidden; diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 38cfc87271..dd5b483ec4 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -12,6 +12,11 @@ border-top: 1px solid $ui-base-color; text-align: start; background: darken($ui-base-color, 4%); + + &.critical { + font-weight: 700; + color: $gold-star; + } } & > thead > tr > th { diff --git a/app/lib/admin/system_check.rb b/app/lib/admin/system_check.rb index 89dfcef9f1..25c88341a4 100644 --- a/app/lib/admin/system_check.rb +++ b/app/lib/admin/system_check.rb @@ -2,6 +2,7 @@ class Admin::SystemCheck ACTIVE_CHECKS = [ + Admin::SystemCheck::SoftwareVersionCheck, Admin::SystemCheck::MediaPrivacyCheck, Admin::SystemCheck::DatabaseSchemaCheck, Admin::SystemCheck::SidekiqProcessCheck, diff --git a/app/lib/admin/system_check/software_version_check.rb b/app/lib/admin/system_check/software_version_check.rb new file mode 100644 index 0000000000..e142feddf0 --- /dev/null +++ b/app/lib/admin/system_check/software_version_check.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Admin::SystemCheck::SoftwareVersionCheck < Admin::SystemCheck::BaseCheck + include RoutingHelper + + def skip? + !current_user.can?(:view_devops) || !SoftwareUpdate.check_enabled? + end + + def pass? + software_updates.empty? + end + + def message + if software_updates.any?(&:urgent?) + Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true) + else + Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path) + end + end + + private + + def software_updates + @software_updates ||= SoftwareUpdate.pending_to_a.filter { |update| update.urgent? || update.patch_type? } + end +end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 5baf9b38a5..990b92c337 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -45,6 +45,22 @@ class AdminMailer < ApplicationMailer end end + def new_software_updates + locale_for_account(@me) do + mail subject: default_i18n_subject(instance: @instance) + end + end + + def new_critical_software_updates + headers['Priority'] = 'urgent' + headers['X-Priority'] = '1' + headers['Importance'] = 'high' + + locale_for_account(@me) do + mail subject: default_i18n_subject(instance: @instance) + end + end + private def process_params diff --git a/app/models/software_update.rb b/app/models/software_update.rb new file mode 100644 index 0000000000..cb3a6df2ae --- /dev/null +++ b/app/models/software_update.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: software_updates +# +# id :bigint(8) not null, primary key +# version :string not null +# urgent :boolean default(FALSE), not null +# type :integer default("patch"), not null +# release_notes :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class SoftwareUpdate < ApplicationRecord + self.inheritance_column = nil + + enum type: { patch: 0, minor: 1, major: 2 }, _suffix: :type + + def gem_version + Gem::Version.new(version) + end + + class << self + def check_enabled? + ENV['UPDATE_CHECK_URL'] != '' + end + + def pending_to_a + return [] unless check_enabled? + + all.to_a.filter { |update| update.gem_version > Mastodon::Version.gem_version } + end + + def urgent_pending? + pending_to_a.any?(&:urgent?) + end + end +end diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 678467c75d..030cbec4d8 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -44,6 +44,7 @@ class UserSettings setting :pending_account, default: true setting :trends, default: true setting :appeal, default: true + setting :software_updates, default: 'critical', in: %w(none critical patch all) end namespace :interactions do diff --git a/app/policies/software_update_policy.rb b/app/policies/software_update_policy.rb new file mode 100644 index 0000000000..dcb565814f --- /dev/null +++ b/app/policies/software_update_policy.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class SoftwareUpdatePolicy < ApplicationPolicy + def index? + role.can?(:view_devops) + end +end diff --git a/app/presenters/initial_state_presenter.rb b/app/presenters/initial_state_presenter.rb index b87cff51e1..222cc8566c 100644 --- a/app/presenters/initial_state_presenter.rb +++ b/app/presenters/initial_state_presenter.rb @@ -3,9 +3,13 @@ class InitialStatePresenter < ActiveModelSerializers::Model attributes :settings, :push_subscription, :token, :current_account, :admin, :owner, :text, :visibility, - :disabled_account, :moved_to_account + :disabled_account, :moved_to_account, :critical_updates_pending def role current_account&.user_role end + + def critical_updates_pending + role&.can?(:view_devops) && SoftwareUpdate.urgent_pending? + end end diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 9660c941d0..56d45c588e 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -7,6 +7,8 @@ class InitialStateSerializer < ActiveModel::Serializer :media_attachments, :settings, :languages + attribute :critical_updates_pending, if: -> { object&.role&.can?(:view_devops) && SoftwareUpdate.check_enabled? } + has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer has_one :role, serializer: REST::RoleSerializer diff --git a/app/services/software_update_check_service.rb b/app/services/software_update_check_service.rb new file mode 100644 index 0000000000..49b92f104d --- /dev/null +++ b/app/services/software_update_check_service.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +class SoftwareUpdateCheckService < BaseService + def call + clean_outdated_updates! + return unless SoftwareUpdate.check_enabled? + + process_update_notices!(fetch_update_notices) + end + + private + + def clean_outdated_updates! + SoftwareUpdate.find_each do |software_update| + software_update.delete if Mastodon::Version.gem_version >= software_update.gem_version + rescue ArgumentError + software_update.delete + end + end + + def fetch_update_notices + Request.new(:get, "#{api_url}?version=#{version}").add_headers('Accept' => 'application/json', 'User-Agent' => 'Mastodon update checker').perform do |res| + return Oj.load(res.body_with_limit, mode: :strict) if res.code == 200 + end + rescue HTTP::Error, OpenSSL::SSL::SSLError, Oj::ParseError + nil + end + + def api_url + ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') + end + + def version + @version ||= Mastodon::Version.to_s.split('+')[0] + end + + def process_update_notices!(update_notices) + return if update_notices.blank? || update_notices['updatesAvailable'].blank? + + # Clear notices that are not listed by the update server anymore + SoftwareUpdate.where.not(version: update_notices['updatesAvailable'].pluck('version')).delete_all + + # Check if any of the notices is new, and issue notifications + known_versions = SoftwareUpdate.where(version: update_notices['updatesAvailable'].pluck('version')).pluck(:version) + new_update_notices = update_notices['updatesAvailable'].filter { |notice| known_versions.exclude?(notice['version']) } + return if new_update_notices.blank? + + new_updates = new_update_notices.map do |notice| + SoftwareUpdate.create!(version: notice['version'], urgent: notice['urgent'], type: notice['type'], release_notes: notice['releaseNotes']) + end + + notify_devops!(new_updates) + end + + def should_notify_user?(user, urgent_version, patch_version) + case user.settings['notification_emails.software_updates'] + when 'none' + false + when 'critical' + urgent_version + when 'patch' + urgent_version || patch_version + when 'all' + true + end + end + + def notify_devops!(new_updates) + has_new_urgent_version = new_updates.any?(&:urgent?) + has_new_patch_version = new_updates.any?(&:patch_type?) + + User.those_who_can(:view_devops).includes(:account).find_each do |user| + next unless should_notify_user?(user, has_new_urgent_version, has_new_patch_version) + + if has_new_urgent_version + AdminMailer.with(recipient: user.account).new_critical_software_updates.deliver_later + else + AdminMailer.with(recipient: user.account).new_software_updates.deliver_later + end + end + end +end diff --git a/app/views/admin/software_updates/index.html.haml b/app/views/admin/software_updates/index.html.haml new file mode 100644 index 0000000000..7a223ee07b --- /dev/null +++ b/app/views/admin/software_updates/index.html.haml @@ -0,0 +1,29 @@ +- content_for :page_title do + = t('admin.software_updates.title') + +.simple_form + %p.lead + = t('admin.software_updates.description') + = link_to t('admin.software_updates.documentation_link'), 'https://docs.joinmastodon.org/admin/upgrading/#automated_checks', target: '_new' + +%hr.spacer + +- unless @software_updates.empty? + .table-wrapper + %table.table + %thead + %tr + %th= t('admin.software_updates.version') + %th= t('admin.software_updates.type') + %th + %th + %tbody + - @software_updates.each do |update| + %tr + %td= update.version + %td= t("admin.software_updates.types.#{update.type}") + - if update.urgent? + %td.critical= t("admin.software_updates.critical_update") + - else + %td + %td= table_link_to 'link', t('admin.software_updates.release_notes'), update.release_notes diff --git a/app/views/admin_mailer/new_critical_software_updates.text.erb b/app/views/admin_mailer/new_critical_software_updates.text.erb new file mode 100644 index 0000000000..c901bc50f7 --- /dev/null +++ b/app/views/admin_mailer/new_critical_software_updates.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_critical_software_updates.body') %> + +<%= raw t('application_mailer.view')%> <%= admin_software_updates_url %> diff --git a/app/views/admin_mailer/new_software_updates.text.erb b/app/views/admin_mailer/new_software_updates.text.erb new file mode 100644 index 0000000000..2fc4d1a5f2 --- /dev/null +++ b/app/views/admin_mailer/new_software_updates.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_software_updates.body') %> + +<%= raw t('application_mailer.view')%> <%= admin_software_updates_url %> diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index 0913bda9ae..5cc101069c 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -22,7 +22,7 @@ .fields-group = ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails') - - if current_user.can?(:manage_reports, :manage_appeals, :manage_users, :manage_taxonomies) + - if current_user.can?(:manage_reports, :manage_appeals, :manage_users, :manage_taxonomies) || (SoftwareUpdate.check_enabled? && current_user.can?(:view_devops)) %h4= t 'notifications.administration_emails' .fields-group @@ -31,6 +31,10 @@ = ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users) = ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies) + - if SoftwareUpdate.check_enabled? && current_user.can?(:view_devops) + .fields-group + = ff.input :'notification_emails.software_updates', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.software_updates.label'), collection: %w(none critical patch all), label_method: ->(setting) { I18n.t("simple_form.labels.notification_emails.software_updates.#{setting}") }, include_blank: false, hint: false + %h4= t 'notifications.other_settings' .fields-group diff --git a/app/workers/scheduler/software_update_check_scheduler.rb b/app/workers/scheduler/software_update_check_scheduler.rb new file mode 100644 index 0000000000..c732bdedc0 --- /dev/null +++ b/app/workers/scheduler/software_update_check_scheduler.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Scheduler::SoftwareUpdateCheckScheduler + include Sidekiq::Worker + + sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.hour.to_i + + def perform + SoftwareUpdateCheckService.new.call + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 693155d6ef..71e5fb843e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -309,6 +309,7 @@ en: unpublish: Unpublish unpublished_msg: Announcement successfully unpublished! updated_msg: Announcement successfully updated! + critical_update_pending: Critical update pending custom_emojis: assign_category: Assign category by_domain: Domain @@ -779,6 +780,18 @@ en: site_uploads: delete: Delete uploaded file destroyed_msg: Site upload successfully deleted! + software_updates: + critical_update: Critical — please update quickly + description: It is recommended to keep your Mastodon installation up to date to benefit from the latest fixes and features. Moreover, it is sometimes critical to update Mastodon in a timely manner to avoid security issues. For these reasons, Mastodon checks for updates every 30 minutes, and will notify you according to your e-mail notification preferences. + documentation_link: Learn more + release_notes: Release notes + title: Available updates + type: Type + types: + major: Major release + minor: Minor release + patch: Patch release — bugfixes and easy to apply changes + version: Version statuses: account: Author application: Application @@ -843,6 +856,12 @@ en: message_html: You haven't defined any server rules. sidekiq_process_check: message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration + software_version_critical_check: + action: See available updates + message_html: A critical Mastodon update is available, please update as quickly as possible. + software_version_patch_check: + action: See available updates + message_html: A bugfix Mastodon update is available. upload_check_privacy_error: action: Check here for more information message_html: "Your web server is misconfigured. The privacy of your users is at risk." @@ -956,6 +975,9 @@ en: body: "%{target} is appealing a moderation decision by %{action_taken_by} from %{date}, which was %{type}. They wrote:" next_steps: You can approve the appeal to undo the moderation decision, or ignore it. subject: "%{username} is appealing a moderation decision on %{instance}" + new_critical_software_updates: + body: New critical versions of Mastodon have been released, you may want to update as soon as possible! + subject: Critical Mastodon updates are available for %{instance}! new_pending_account: body: The details of the new account are below. You can approve or reject this application. subject: New account up for review on %{instance} (%{username}) @@ -963,6 +985,9 @@ en: body: "%{reporter} has reported %{target}" body_remote: Someone from %{domain} has reported %{target} subject: New report for %{instance} (#%{id}) + new_software_updates: + body: New Mastodon versions have been released, you may want to update! + subject: New Mastodon versions are available for %{instance}! new_trends: body: 'The following items need a review before they can be displayed publicly:' new_trending_links: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index b1297606bc..0b718c5b65 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -291,6 +291,12 @@ en: pending_account: New account needs review reblog: Someone boosted your post report: New report is submitted + software_updates: + all: Notify on all updates + critical: Notify on critical updates only + label: A new Mastodon version is available + none: Never notify of updates (not recommended) + patch: Notify on bugfix updates trending_tag: New trend requires review rule: text: Rule diff --git a/config/navigation.rb b/config/navigation.rb index f608c2eea7..e86c695a98 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -3,6 +3,9 @@ SimpleNavigation::Configuration.run do |navigation| navigation.items do |n| n.item :web, safe_join([fa_icon('chevron-left fw'), t('settings.back')]), root_path + + n.item :software_updates, safe_join([fa_icon('exclamation-circle fw'), t('admin.critical_update_pending')]), admin_software_updates_path, if: -> { ENV['UPDATE_CHECK_URL'] != '' && current_user.can?(:view_devops) && SoftwareUpdate.urgent_pending? }, html: { class: 'warning' } + n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_path, if: -> { current_user.functional? }, highlights_on: %r{/settings/profile|/settings/featured_tags|/settings/verification|/settings/privacy} n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_path, if: -> { current_user.functional? } do |s| diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 4573878ede..207cb0580d 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -201,4 +201,6 @@ namespace :admin do end end end + + resources :software_updates, only: [:index] end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 12c45c22a1..f1ba5651dd 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -58,3 +58,7 @@ interval: 1 minute class: Scheduler::SuspendedUserCleanupScheduler queue: scheduler + software_update_check_scheduler: + interval: 30 minutes + class: Scheduler::SoftwareUpdateCheckScheduler + queue: scheduler diff --git a/db/migrate/20230822081029_create_software_updates.rb b/db/migrate/20230822081029_create_software_updates.rb new file mode 100644 index 0000000000..146d5d3037 --- /dev/null +++ b/db/migrate/20230822081029_create_software_updates.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateSoftwareUpdates < ActiveRecord::Migration[7.0] + def change + create_table :software_updates do |t| + t.string :version, null: false + t.boolean :urgent, default: false, null: false + t.integer :type, default: 0, null: false + t.string :release_notes, default: '', null: false + + t.timestamps + end + + add_index :software_updates, :version, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8b758fc7df..c861069420 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[7.0].define(version: 2023_08_18_142253) do +ActiveRecord::Schema[7.0].define(version: 2023_08_22_081029) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -903,6 +903,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_18_142253) do t.index ["var"], name: "index_site_uploads_on_var", unique: true end + create_table "software_updates", force: :cascade do |t| + t.string "version", null: false + t.boolean "urgent", default: false, null: false + t.integer "type", default: 0, null: false + t.string "release_notes", default: "", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["version"], name: "index_software_updates_on_version", unique: true + end + create_table "status_edits", force: :cascade do |t| t.bigint "status_id", null: false t.bigint "account_id" diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index c542d5d49a..65f90f93fd 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -39,6 +39,10 @@ module Mastodon components.join end + def gem_version + @gem_version ||= Gem::Version.new(to_s.split('+')[0]) + end + def repository ENV.fetch('GITHUB_REPOSITORY', 'mastodon/mastodon') end diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 010caaf8ea..f68d1cf1f8 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -424,6 +424,10 @@ namespace :mastodon do end end + prompt.say "\n" + + env['UPDATE_CHECK_URL'] = '' unless prompt.yes?('Do you want Mastodon to periodically check for important updates and notify you? (Recommended)', default: true) + prompt.say "\n" prompt.say 'This configuration will be written to .env.production' diff --git a/spec/fabricators/software_update_fabricator.rb b/spec/fabricators/software_update_fabricator.rb new file mode 100644 index 0000000000..622fff66e8 --- /dev/null +++ b/spec/fabricators/software_update_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:software_update) do + version '99.99.99' + urgent false + type 'patch' +end diff --git a/spec/features/admin/software_updates_spec.rb b/spec/features/admin/software_updates_spec.rb new file mode 100644 index 0000000000..4a635d1a79 --- /dev/null +++ b/spec/features/admin/software_updates_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'finding software updates through the admin interface' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true, release_notes: 'https://github.com/mastodon/mastodon/releases/v99') + + sign_in Fabricate(:user, role: UserRole.find_by(name: 'Owner')), scope: :user + end + + it 'shows a link to the software updates page, which links to release notes' do + visit settings_profile_path + click_on I18n.t('admin.critical_update_pending') + + expect(page).to have_title(I18n.t('admin.software_updates.title')) + + expect(page).to have_content('99.99.99') + + click_on I18n.t('admin.software_updates.release_notes') + expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true) + end +end diff --git a/spec/lib/admin/system_check/software_version_check_spec.rb b/spec/lib/admin/system_check/software_version_check_spec.rb new file mode 100644 index 0000000000..de4335fc51 --- /dev/null +++ b/spec/lib/admin/system_check/software_version_check_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::SystemCheck::SoftwareVersionCheck do + include RoutingHelper + + subject(:check) { described_class.new(user) } + + let(:user) { Fabricate(:user) } + + describe 'skip?' do + context 'when user cannot view devops' do + before { allow(user).to receive(:can?).with(:view_devops).and_return(false) } + + it 'returns true' do + expect(check.skip?).to be true + end + end + + context 'when user can view devops' do + before { allow(user).to receive(:can?).with(:view_devops).and_return(true) } + + it 'returns false' do + expect(check.skip?).to be false + end + + context 'when checks are disabled' do + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: '' do + example.run + end + end + + it 'returns true' do + expect(check.skip?).to be true + end + end + end + end + + describe 'pass?' do + context 'when there is no known update' do + it 'returns true' do + expect(check.pass?).to be true + end + end + + context 'when there is a non-urgent major release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: false) + end + + it 'returns true' do + expect(check.pass?).to be true + end + end + + context 'when there is an urgent major release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is an urgent minor release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'minor', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is an urgent patch release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is a non-urgent patch release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + end + + describe 'message' do + context 'when there is a non-urgent patch release pending' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false) + end + + it 'sends class name symbol to message instance' do + allow(Admin::SystemCheck::Message).to receive(:new) + .with(:software_version_patch_check, anything, anything) + + check.message + + expect(Admin::SystemCheck::Message).to have_received(:new) + .with(:software_version_patch_check, nil, admin_software_updates_path) + end + end + + context 'when there is an urgent patch release pending' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true) + end + + it 'sends class name symbol to message instance' do + allow(Admin::SystemCheck::Message).to receive(:new) + .with(:software_version_critical_check, anything, anything, anything) + + check.message + + expect(Admin::SystemCheck::Message).to have_received(:new) + .with(:software_version_critical_check, nil, admin_software_updates_path, true) + end + end + end +end diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb index 9123804a48..423dce88ab 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -85,4 +85,46 @@ RSpec.describe AdminMailer do expect(mail.body.encoded).to match 'The following items need a review before they can be displayed publicly' end end + + describe '.new_software_updates' do + let(:recipient) { Fabricate(:account, username: 'Bob') } + let(:mail) { described_class.with(recipient: recipient).new_software_updates } + + before do + recipient.user.update(locale: :en) + end + + it 'renders the headers' do + expect(mail.subject).to eq('New Mastodon versions are available for cb6e6126.ngrok.io!') + expect(mail.to).to eq [recipient.user_email] + expect(mail.from).to eq ['notifications@localhost'] + end + + it 'renders the body' do + expect(mail.body.encoded).to match 'New Mastodon versions have been released, you may want to update!' + end + end + + describe '.new_critical_software_updates' do + let(:recipient) { Fabricate(:account, username: 'Bob') } + let(:mail) { described_class.with(recipient: recipient).new_critical_software_updates } + + before do + recipient.user.update(locale: :en) + end + + it 'renders the headers', :aggregate_failures do + expect(mail.subject).to eq('Critical Mastodon updates are available for cb6e6126.ngrok.io!') + expect(mail.to).to eq [recipient.user_email] + expect(mail.from).to eq ['notifications@localhost'] + + expect(mail['Importance'].value).to eq 'high' + expect(mail['Priority'].value).to eq 'urgent' + expect(mail['X-Priority'].value).to eq '1' + end + + it 'renders the body' do + expect(mail.body.encoded).to match 'New critical versions of Mastodon have been released, you may want to update as soon as possible!' + end + end end diff --git a/spec/models/software_update_spec.rb b/spec/models/software_update_spec.rb new file mode 100644 index 0000000000..0a494b0c4c --- /dev/null +++ b/spec/models/software_update_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SoftwareUpdate do + describe '.pending_to_a' do + before do + allow(Mastodon::Version).to receive(:gem_version).and_return(Gem::Version.new(mastodon_version)) + + Fabricate(:software_update, version: '3.4.42', type: 'patch', urgent: true) + Fabricate(:software_update, version: '3.5.0', type: 'minor', urgent: false) + Fabricate(:software_update, version: '4.2.0', type: 'major', urgent: false) + end + + context 'when the Mastodon version is an outdated release' do + let(:mastodon_version) { '3.4.0' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('3.4.42', '3.5.0', '4.2.0') + end + end + + context 'when the Mastodon version is more recent than anything last returned by the server' do + let(:mastodon_version) { '5.0.0' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to eq [] + end + end + + context 'when the Mastodon version is an outdated nightly' do + let(:mastodon_version) { '4.3.0-nightly.2023-09-10' } + + before do + Fabricate(:software_update, version: '4.3.0-nightly.2023-09-12', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-nightly.2023-09-12') + end + end + + context 'when the Mastodon version is a very outdated nightly' do + let(:mastodon_version) { '4.2.0-nightly.2023-07-10' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.2.0') + end + end + + context 'when the Mastodon version is an outdated dev version' do + let(:mastodon_version) { '4.3.0-0.dev.0' } + + before do + Fabricate(:software_update, version: '4.3.0-0.dev.2', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-0.dev.2') + end + end + + context 'when the Mastodon version is an outdated beta version' do + let(:mastodon_version) { '4.3.0-beta1' } + + before do + Fabricate(:software_update, version: '4.3.0-beta2', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-beta2') + end + end + + context 'when the Mastodon version is an outdated beta version and there is a rc' do + let(:mastodon_version) { '4.3.0-beta1' } + + before do + Fabricate(:software_update, version: '4.3.0-rc1', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-rc1') + end + end + end +end diff --git a/spec/policies/software_update_policy_spec.rb b/spec/policies/software_update_policy_spec.rb new file mode 100644 index 0000000000..e19ba61612 --- /dev/null +++ b/spec/policies/software_update_policy_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'pundit/rspec' + +RSpec.describe SoftwareUpdatePolicy do + subject { described_class } + + let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')).account } + let(:john) { Fabricate(:account) } + + permissions :index? do + context 'when owner' do + it 'permits' do + expect(subject).to permit(admin, SoftwareUpdate) + end + end + + context 'when not owner' do + it 'denies' do + expect(subject).to_not permit(john, SoftwareUpdate) + end + end + end +end diff --git a/spec/services/software_update_check_service_spec.rb b/spec/services/software_update_check_service_spec.rb new file mode 100644 index 0000000000..c8821348ac --- /dev/null +++ b/spec/services/software_update_check_service_spec.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SoftwareUpdateCheckService, type: :service do + subject { described_class.new } + + shared_examples 'when the feature is enabled' do + let(:full_update_check_url) { "#{update_check_url}?version=#{Mastodon::Version.to_s.split('+')[0]}" } + + let(:devops_role) { Fabricate(:user_role, name: 'DevOps', permissions: UserRole::FLAGS[:view_devops]) } + let(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) } + let(:old_devops_user) { Fabricate(:user) } + let(:none_user) { Fabricate(:user, role: devops_role) } + let(:patch_user) { Fabricate(:user, role: devops_role) } + let(:critical_user) { Fabricate(:user, role: devops_role) } + + around do |example| + queue_adapter = ActiveJob::Base.queue_adapter + ActiveJob::Base.queue_adapter = :test + + example.run + + ActiveJob::Base.queue_adapter = queue_adapter + end + + before do + Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false) + Fabricate(:software_update, version: '42.13.12', type: 'major', urgent: false) + + owner_user.settings.update('notification_emails.software_updates': 'all') + owner_user.save! + + old_devops_user.settings.update('notification_emails.software_updates': 'all') + old_devops_user.save! + + none_user.settings.update('notification_emails.software_updates': 'none') + none_user.save! + + patch_user.settings.update('notification_emails.software_updates': 'patch') + patch_user.save! + + critical_user.settings.update('notification_emails.software_updates': 'critical') + critical_user.save! + end + + context 'when the update server errors out' do + before do + stub_request(:get, full_update_check_url).to_return(status: 404) + end + + it 'deletes outdated update records but keeps valid update records' do + expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['42.13.12']) + end + end + + context 'when the server returns new versions' do + let(:server_json) do + { + updatesAvailable: [ + { + version: '4.2.1', + urgent: false, + type: 'patch', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.2.1', + }, + { + version: '4.3.0', + urgent: false, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.3.0', + }, + { + version: '5.0.0', + urgent: false, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0', + }, + ], + } + end + + before do + stub_request(:get, full_update_check_url).to_return(body: Oj.dump(server_json)) + end + + it 'updates the list of known updates' do + expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['4.2.1', '4.3.0', '5.0.0']) + end + + context 'when no update is urgent' do + it 'sends e-mail notifications according to settings', :aggregate_failures do + expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_software_updates) + .with(hash_including(params: { recipient: owner_user.account })).once + .and(have_enqueued_mail(AdminMailer, :new_software_updates).with(hash_including(params: { recipient: patch_user.account })).once) + .and(have_enqueued_mail.at_most(2)) + end + end + + context 'when an update is urgent' do + let(:server_json) do + { + updatesAvailable: [ + { + version: '5.0.0', + urgent: true, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0', + }, + ], + } + end + + it 'sends e-mail notifications according to settings', :aggregate_failures do + expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_critical_software_updates) + .with(hash_including(params: { recipient: owner_user.account })).once + .and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: patch_user.account })).once) + .and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: critical_user.account })).once) + .and(have_enqueued_mail.at_most(3)) + end + end + end + end + + context 'when update checking is disabled' do + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: '' do + example.run + end + end + + before do + Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false) + end + + it 'deletes outdated update records' do + expect { subject.call }.to change(SoftwareUpdate, :count).from(1).to(0) + end + end + + context 'when using the default update checking API' do + let(:update_check_url) { 'https://api.joinmastodon.org/update-check' } + + it_behaves_like 'when the feature is enabled' + end + + context 'when using a custom update check URL' do + let(:update_check_url) { 'https://api.example.com/update_check' } + + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: 'https://api.example.com/update_check' do + example.run + end + end + + it_behaves_like 'when the feature is enabled' + end +end diff --git a/spec/workers/scheduler/software_update_check_scheduler_spec.rb b/spec/workers/scheduler/software_update_check_scheduler_spec.rb new file mode 100644 index 0000000000..f596c0a1ec --- /dev/null +++ b/spec/workers/scheduler/software_update_check_scheduler_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Scheduler::SoftwareUpdateCheckScheduler do + subject { described_class.new } + + describe 'perform' do + let(:service_double) { instance_double(SoftwareUpdateCheckService, call: nil) } + + before do + allow(SoftwareUpdateCheckService).to receive(:new).and_return(service_double) + end + + it 'calls SoftwareUpdateCheckService' do + subject.perform + expect(service_double).to have_received(:call) + end + end +end From 1b7b338d0a8fd34824aed80327d62526da9c5eac Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 2 Sep 2023 09:02:44 +0200 Subject: [PATCH 071/182] Fix some video encoding failing due to uneven dimensions (#26766) --- app/models/media_attachment.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 984f4252a1..f0b072e23f 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -102,6 +102,7 @@ class MediaAttachment < ApplicationRecord 'preset' => 'veryfast', 'movflags' => 'faststart', # Move metadata to start of file so playback can begin before download finishes 'pix_fmt' => 'yuv420p', # Ensure color space for cross-browser compatibility + 'vf' => 'crop=floor(iw/2)*2:floor(ih/2)*2', # h264 requires width and height to be even. Crop instead of scale to avoid blurring 'c:v' => 'h264', 'c:a' => 'aac', 'b:a' => '192k', From f188aa636cbc59efd7325114171b7fc1e16f7c47 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 11:31:20 +0200 Subject: [PATCH 072/182] [Glitch] Add `data-nosnippet` attribute to remote posts and local posts with `noindex` Port d8276ad1b43681a90825a8c1f9d4bcc8b8f302c0 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/flavours/glitch/components/status.jsx b/app/javascript/flavours/glitch/components/status.jsx index d3d432ae05..36abc69930 100644 --- a/app/javascript/flavours/glitch/components/status.jsx +++ b/app/javascript/flavours/glitch/components/status.jsx @@ -792,6 +792,7 @@ class Status extends ImmutablePureComponent { tabIndex={0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))} + data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined} > {!muted && prepend} From dab6bbdd547d2b5993e2e738f38cb92b4bbe955d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 13:18:39 +0200 Subject: [PATCH 073/182] [Glitch] Add search options to search popout in web UI Port 9ab2c6b4792f7f3ed07a460a3fdf391062127268 to glitch-soc Signed-off-by: Claire --- .../features/compose/components/search.jsx | 50 ++++++++++++++++--- .../glitch/styles/components/search.scss | 6 +++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index 2f1b46e5d9..218518a7b6 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -1,11 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { - injectIntl, - FormattedMessage, - defineMessages, -} from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl'; import classNames from 'classnames'; @@ -52,6 +48,16 @@ class Search extends PureComponent { options: [], }; + defaultOptions = [ + { label: <>has: , action: e => { e.preventDefault(); this._insertText('has:') } }, + { label: <>is: , action: e => { e.preventDefault(); this._insertText('is:') } }, + { label: <>language: , action: e => { e.preventDefault(); this._insertText('language:') } }, + { label: <>from: , action: e => { e.preventDefault(); this._insertText('from:') } }, + { label: <>before: , action: e => { e.preventDefault(); this._insertText('before:') } }, + { label: <>during: , action: e => { e.preventDefault(); this._insertText('during:') } }, + { label: <>after: , action: e => { e.preventDefault(); this._insertText('after:') } }, + ]; + setRef = c => { this.searchForm = c; }; @@ -100,7 +106,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions(); + const options = this._getOptions().concat(this.defaultOptions); switch(e.key) { case 'Escape': @@ -131,10 +137,9 @@ class Search extends PureComponent { if (selectedOption === -1) { this._submit(); } else if (options.length > 0) { - options[selectedOption].action(); + options[selectedOption].action(e); } - this._unfocus(); break; case 'Delete': if (selectedOption > -1 && options.length > 0) { @@ -161,6 +166,7 @@ class Search extends PureComponent { router.history.push(`/tags/${query}`); onClickSearchResult(query, 'hashtag'); + this._unfocus(); }; handleAccountClick = () => { @@ -171,6 +177,7 @@ class Search extends PureComponent { router.history.push(`/@${query}`); onClickSearchResult(query, 'account'); + this._unfocus(); }; handleURLClick = () => { @@ -178,6 +185,7 @@ class Search extends PureComponent { const { onOpenURL } = this.props; onOpenURL(router.history); + this._unfocus(); }; handleStatusSearch = () => { @@ -196,6 +204,8 @@ class Search extends PureComponent { } else if (search.get('type') === 'hashtag') { router.history.push(`/tags/${search.get('q')}`); } + + this._unfocus(); }; handleForgetRecentSearchClick = search => { @@ -208,6 +218,18 @@ class Search extends PureComponent { document.querySelector('.ui').parentElement.focus(); } + _insertText (text) { + const { value, onChange } = this.props; + + if (value === '') { + onChange(text); + } else if (value[value.length - 1] === ' ') { + onChange(`${value}${text}`); + } else { + onChange(`${value} ${text}`); + } + } + _submit (type) { const { onSubmit, openInRoute } = this.props; const { router } = this.context; @@ -217,6 +239,8 @@ class Search extends PureComponent { if (openInRoute) { router.history.push('/search'); } + + this._unfocus(); } _getOptions () { @@ -337,6 +361,16 @@ class Search extends PureComponent { )} + +

+ +
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
); diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss index 99ed697e37..be35077dcd 100644 --- a/app/javascript/flavours/glitch/styles/components/search.scss +++ b/app/javascript/flavours/glitch/styles/components/search.scss @@ -25,6 +25,12 @@ } &__menu { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + &__message { color: $dark-text-color; padding: 0 10px; From 97d464e5d3b893ca19b961895741ad9252996109 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 03:56:19 -0500 Subject: [PATCH 074/182] [Glitch] Fix bug with favourited view on Toots only showing latest favouriting accounts Port 1b52b02c1c8cd8c892cea374e46bcd2ce183d023 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/interactions.js | 58 ++++++++++++++++++- .../glitch/features/favourites/index.jsx | 25 +++++--- .../flavours/glitch/reducers/user_lists.js | 15 ++++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 6b8864a039..78f7741ab8 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -1,5 +1,6 @@ -import api from '../api'; +import api, { getLinks } from '../api'; +import { fetchRelationships } from './accounts'; import { importFetchedAccounts, importFetchedStatus } from './importer'; export const REBLOG_REQUEST = 'REBLOG_REQUEST'; @@ -26,6 +27,10 @@ export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST'; export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS'; export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL'; +export const FAVOURITES_EXPAND_REQUEST = 'FAVOURITES_EXPAND_REQUEST'; +export const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS'; +export const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL'; + export const PIN_REQUEST = 'PIN_REQUEST'; export const PIN_SUCCESS = 'PIN_SUCCESS'; export const PIN_FAIL = 'PIN_FAIL'; @@ -294,8 +299,10 @@ export function fetchFavourites(id) { dispatch(fetchFavouritesRequest(id)); api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFavouritesSuccess(id, response.data)); + dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFavouritesFail(id, error)); }); @@ -309,17 +316,62 @@ export function fetchFavouritesRequest(id) { }; } -export function fetchFavouritesSuccess(id, accounts) { +export function fetchFavouritesSuccess(id, accounts, next) { return { type: FAVOURITES_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchFavouritesFail(id, error) { return { type: FAVOURITES_FETCH_FAIL, + id, + error, + }; +} + +export function expandFavourites(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'favourited_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandFavouritesRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandFavouritesFail(id, error))); + }; +} + +export function expandFavouritesRequest(id) { + return { + type: FAVOURITES_EXPAND_REQUEST, + id, + }; +} + +export function expandFavouritesSuccess(id, accounts, next) { + return { + type: FAVOURITES_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandFavouritesFail(id, error) { + return { + type: FAVOURITES_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/flavours/glitch/features/favourites/index.jsx b/app/javascript/flavours/glitch/features/favourites/index.jsx index 2b36945eee..49fd62b966 100644 --- a/app/javascript/flavours/glitch/features/favourites/index.jsx +++ b/app/javascript/flavours/glitch/features/favourites/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchFavourites } from 'flavours/glitch/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/interactions'; import ColumnHeader from 'flavours/glitch/components/column_header'; import { Icon } from 'flavours/glitch/components/icon'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; @@ -23,7 +25,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true), }); class Favourites extends ImmutablePureComponent { @@ -32,6 +36,8 @@ class Favourites extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -42,12 +48,6 @@ class Favourites extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps (nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchFavourites(nextProps.params.statusId)); - } - } - handleHeaderClick = () => { this.column.scrollTop(); }; @@ -60,8 +60,12 @@ class Favourites extends ImmutablePureComponent { this.props.dispatch(fetchFavourites(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandFavourites(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -87,6 +91,9 @@ class Favourites extends ImmutablePureComponent { /> diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index dd240e99d4..a10fed4334 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -45,7 +45,12 @@ import { } from 'flavours/glitch/actions/featured_tags'; import { REBLOGS_FETCH_SUCCESS, + FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, + FAVOURITES_FETCH_FAIL, + FAVOURITES_EXPAND_REQUEST, + FAVOURITES_EXPAND_SUCCESS, + FAVOURITES_EXPAND_FAIL, } from 'flavours/glitch/actions/interactions'; import { MUTES_FETCH_REQUEST, @@ -135,7 +140,15 @@ export default function userLists(state = initialState, action) { case REBLOGS_FETCH_SUCCESS: return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_EXPAND_SUCCESS: + return appendToList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_FETCH_REQUEST: + case FAVOURITES_EXPAND_REQUEST: + return state.setIn(['favourited_by', action.id, 'isLoading'], true); + case FAVOURITES_FETCH_FAIL: + case FAVOURITES_EXPAND_FAIL: + return state.setIn(['favourited_by', action.id, 'isLoading'], false); case NOTIFICATIONS_UPDATE: return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; case FOLLOW_REQUESTS_FETCH_SUCCESS: From e21ef86a9b02db40077ea71794267419280a63cb Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 07:42:20 -0500 Subject: [PATCH 075/182] [Glitch] Fix bug with reblogged view on Toots only showing latest reblogging accounts Port 378b3dcddfb17bc942041b584985a79802869c98 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/interactions.js | 55 ++++++++++++++++++- .../glitch/features/reblogs/index.jsx | 29 +++++----- .../flavours/glitch/reducers/user_lists.js | 15 ++++- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 78f7741ab8..095fb3155e 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -7,6 +7,10 @@ export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_FAIL = 'REBLOG_FAIL'; +export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; +export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; +export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; + export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; @@ -264,8 +268,10 @@ export function fetchReblogs(id) { dispatch(fetchReblogsRequest(id)); api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchReblogsSuccess(id, response.data)); + dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchReblogsFail(id, error)); }); @@ -279,17 +285,62 @@ export function fetchReblogsRequest(id) { }; } -export function fetchReblogsSuccess(id, accounts) { +export function fetchReblogsSuccess(id, accounts, next) { return { type: REBLOGS_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchReblogsFail(id, error) { return { type: REBLOGS_FETCH_FAIL, + id, + error, + }; +} + +export function expandReblogs(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'reblogged_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandReblogsRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandReblogsFail(id, error))); + }; +} + +export function expandReblogsRequest(id) { + return { + type: REBLOGS_EXPAND_REQUEST, + id, + }; +} + +export function expandReblogsSuccess(id, accounts, next) { + return { + type: REBLOGS_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandReblogsFail(id, error) { + return { + type: REBLOGS_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/flavours/glitch/features/reblogs/index.jsx b/app/javascript/flavours/glitch/features/reblogs/index.jsx index 90d10db628..8cc4c004f0 100644 --- a/app/javascript/flavours/glitch/features/reblogs/index.jsx +++ b/app/javascript/flavours/glitch/features/reblogs/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchReblogs } from 'flavours/glitch/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchReblogs, expandReblogs } from 'flavours/glitch/actions/interactions'; import ColumnHeader from 'flavours/glitch/components/column_header'; import { Icon } from 'flavours/glitch/components/icon'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; @@ -16,17 +18,15 @@ import ScrollableList from 'flavours/glitch/components/scrollable_list'; import AccountContainer from 'flavours/glitch/containers/account_container'; import Column from 'flavours/glitch/features/ui/components/column'; - - - - const messages = defineMessages({ heading: { id: 'column.reblogged_by', defaultMessage: 'Boosted by' }, refresh: { id: 'refresh', defaultMessage: 'Refresh' }, }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'isLoading'], true), }); class Reblogs extends ImmutablePureComponent { @@ -35,6 +35,8 @@ class Reblogs extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -45,12 +47,6 @@ class Reblogs extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchReblogs(nextProps.params.statusId)); - } - } - handleHeaderClick = () => { this.column.scrollTop(); }; @@ -63,8 +59,12 @@ class Reblogs extends ImmutablePureComponent { this.props.dispatch(fetchReblogs(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandReblogs(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -91,6 +91,9 @@ class Reblogs extends ImmutablePureComponent { diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index a10fed4334..d37451d005 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -44,7 +44,12 @@ import { FEATURED_TAGS_FETCH_FAIL, } from 'flavours/glitch/actions/featured_tags'; import { + REBLOGS_FETCH_REQUEST, REBLOGS_FETCH_SUCCESS, + REBLOGS_FETCH_FAIL, + REBLOGS_EXPAND_REQUEST, + REBLOGS_EXPAND_SUCCESS, + REBLOGS_EXPAND_FAIL, FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, FAVOURITES_FETCH_FAIL, @@ -138,7 +143,15 @@ export default function userLists(state = initialState, action) { case FOLLOWING_EXPAND_FAIL: return state.setIn(['following', action.id, 'isLoading'], false); case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_EXPAND_SUCCESS: + return appendToList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_FETCH_REQUEST: + case REBLOGS_EXPAND_REQUEST: + return state.setIn(['reblogged_by', action.id, 'isLoading'], true); + case REBLOGS_FETCH_FAIL: + case REBLOGS_EXPAND_FAIL: + return state.setIn(['reblogged_by', action.id, 'isLoading'], false); case FAVOURITES_FETCH_SUCCESS: return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); case FAVOURITES_EXPAND_SUCCESS: From 513d22e1d685ace32efbbe770f2044231e1fc085 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 16:29:52 +0200 Subject: [PATCH 076/182] [Glitch] Fix sign up steps progress layout in right-to-left locales Port 4452d8dad567f8c459d4edb2cb1967c9f8e35cd7 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/forms.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 2b7c02f115..7d2f5c1bd2 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -1187,14 +1187,14 @@ code { } li:first-child .label { - left: auto; inset-inline-start: 0; + inset-inline-end: auto; text-align: start; transform: none; } li:last-child .label { - left: auto; + inset-inline-start: auto; inset-inline-end: 0; text-align: end; transform: none; From f7e8c8033ff46f0f965367c5655710c1299d59cc Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Thu, 31 Aug 2023 12:18:46 +0200 Subject: [PATCH 077/182] [Glitch] Add an explanation banner on switching to single column mode Port 2c0eaf50f6441da34783d982519889818901d467 to glitch-soc Signed-off-by: Claire --- .../features/ui/components/navigation_panel.jsx | 11 ++++++++--- .../glitch/styles/components/columns.scss | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index c0726404ee..f6984d5adb 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -29,6 +29,7 @@ const messages = defineMessages({ about: { id: 'navigation_bar.about', defaultMessage: 'About' }, search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, + openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' }, app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' }, }); @@ -56,9 +57,13 @@ class NavigationPanel extends Component {
{transientSingleColumn && (
- - {intl.formatMessage(messages.advancedInterface)} - +
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +

)} diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index d4860258ed..6c97137964 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -228,6 +228,22 @@ $ui-header-height: 55px; top: -48px; } +.switch-to-advanced { + color: $classic-primary-color; + background-color: $classic-base-color; + padding: 15px; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 12px; + font-size: 13px; + line-height: 18px; + + .switch-to-advanced__toggle { + color: $ui-button-tertiary-color; + font-weight: bold; + } +} + .column-link { background: lighten($ui-base-color, 8%); color: $primary-text-color; From 64e558cbbf4f523dc49d6cd621bb6ef81e11abdf Mon Sep 17 00:00:00 2001 From: Santiago Kozak Date: Thu, 31 Aug 2023 09:15:58 -0300 Subject: [PATCH 078/182] [Glitch] Allow filter form in profiles directory to wrap Port 1b9fe4882f3513c0ad9bb049e336f45fffca2a85 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/styles/components/single_column.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss index 2f8f7e2dd2..7efcf0c097 100644 --- a/app/javascript/flavours/glitch/styles/components/single_column.scss +++ b/app/javascript/flavours/glitch/styles/components/single_column.scss @@ -120,6 +120,7 @@ .filter-form { display: flex; + flex-wrap: wrap; } .autosuggest-textarea__textarea { From e070644a5d65613f79a870dbc1af3392a34b213b Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:13:27 +0200 Subject: [PATCH 079/182] [Glitch] Fix search popout including full-text search instructions when full-text search is disabled Port 20913b52bb192a9525b3da53a1d25c816a4a177d to glitch-soc Signed-off-by: Claire --- .../features/compose/components/search.jsx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index 218518a7b6..e83c1761ec 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -106,7 +106,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions().concat(this.defaultOptions); + const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions(); switch(e.key) { case 'Escape': @@ -362,15 +362,19 @@ class Search extends PureComponent { )} -

+ {searchEnabled && ( + <> +

-
- {this.defaultOptions.map(({ key, label, action }, i) => ( - - ))} -
+
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
+ + )}
); From f3f3294db5c71f8b6a49555d00b27bd1fcf9cd32 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 2 Sep 2023 14:23:18 +0200 Subject: [PATCH 080/182] Fix test failures due to different default settings in glitch-soc --- spec/controllers/api/v1/timelines/tag_controller_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 1c60798fcf..8896f02a77 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -22,6 +22,10 @@ describe Api::V1::Timelines::TagController do end context 'when the instance allows public preview' do + before do + Setting.timeline_preview = true + end + context 'when the user is not authenticated' do let(:token) { nil } From 70c734d65a9c9cd2a84dc333dcda5196e0584f24 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 2 Sep 2023 14:46:26 +0200 Subject: [PATCH 081/182] Fix wrong color on active icons with counters in web UI (#26767) --- .../styles/mastodon/components.scss | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 10083a2a32..ef2b27d6a8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -228,20 +228,20 @@ color: lighten($lighter-text-color, 7%); background-color: transparent; } + } - &.active { + &.active { + color: $highlight-text-color; + + &:hover, + &:active, + &:focus { color: $highlight-text-color; + background-color: transparent; + } - &:hover, - &:active, - &:focus { - color: $highlight-text-color; - background-color: transparent; - } - - &.disabled { - color: lighten($highlight-text-color, 13%); - } + &.disabled { + color: lighten($highlight-text-color, 13%); } } From fa329286d0d1f76a5e033eeda77b61fb8a7b2d44 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:41:10 +0200 Subject: [PATCH 082/182] [Glitch] Add `authorized_fetch` server setting in addition to env var Port SCSS changes from 7d3930a52059f023cc88d76abd95208a74ca6ccd to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/accounts.scss | 2 ++ app/javascript/flavours/glitch/styles/forms.scss | 1 + 2 files changed, 3 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/accounts.scss b/app/javascript/flavours/glitch/styles/accounts.scss index ad0dfe0177..b0fe21bcf1 100644 --- a/app/javascript/flavours/glitch/styles/accounts.scss +++ b/app/javascript/flavours/glitch/styles/accounts.scss @@ -192,6 +192,8 @@ } .account-role, +.information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended, .simple_form .glitch_only { diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 7d2f5c1bd2..b8fc4a653f 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -103,6 +103,7 @@ code { } } + .overridden, .recommended, .not_recommended, .glitch_only { From f15e8e3e0d403494aebd19177aafbc29bc79ff9e Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 17:47:07 +0200 Subject: [PATCH 083/182] [Glitch] Add admin notifications for new Mastodon versions Port front-end changes from 97238e3496a87a232fba195be5805f29169513a4 to glitch-soc Signed-off-by: Claire --- .../components/critical_update_banner.tsx | 26 +++++++++++++++++++ .../glitch/features/home_timeline/index.jsx | 14 +++++++--- .../flavours/glitch/initial_state.js | 2 ++ .../flavours/glitch/styles/admin.scss | 5 ++++ .../glitch/styles/components/columns.scss | 18 ++++++++++++- .../flavours/glitch/styles/tables.scss | 5 ++++ 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx diff --git a/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx b/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx new file mode 100644 index 0000000000..d0dd2b6acd --- /dev/null +++ b/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx @@ -0,0 +1,26 @@ +import { FormattedMessage } from 'react-intl'; + +export const CriticalUpdateBanner = () => ( +
+
+

+ +

+

+ {' '} + + + +

+
+
+); diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.jsx b/app/javascript/flavours/glitch/features/home_timeline/index.jsx index e17680d8bb..80dae5e4d0 100644 --- a/app/javascript/flavours/glitch/features/home_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/home_timeline/index.jsx @@ -14,7 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/act import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container'; -import { me } from 'flavours/glitch/initial_state'; +import { me, criticalUpdatesPending } from 'flavours/glitch/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { expandHomeTimeline } from '../../actions/timelines'; @@ -23,6 +23,7 @@ import ColumnHeader from '../../components/column_header'; import StatusListContainer from '../ui/containers/status_list_container'; import { ColumnSettings } from './components/column_settings'; +import { CriticalUpdateBanner } from './components/critical_update_banner'; import { ExplorePrompt } from './components/explore_prompt'; const messages = defineMessages({ @@ -158,8 +159,9 @@ class HomeTimeline extends PureComponent { const { intl, hasUnread, columnId, multiColumn, tooSlow, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; const { signedIn } = this.context.identity; + const banners = []; - let announcementsButton, banner; + let announcementsButton; if (hasAnnouncements) { announcementsButton = ( @@ -174,8 +176,12 @@ class HomeTimeline extends PureComponent { ); } + if (criticalUpdatesPending) { + banners.push(); + } + if (tooSlow) { - banner = ; + banners.push(); } return ( @@ -197,7 +203,7 @@ class HomeTimeline extends PureComponent { {signedIn ? ( } accounts * @property {InitialStateLanguage[]} languages + * @property {boolean=} critical_updates_pending * @property {InitialStateMeta} meta * @property {object} local_settings * @property {number} max_toot_chars @@ -160,6 +161,7 @@ export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); export const languages = initialState?.languages; +export const criticalUpdatesPending = initialState?.critical_updates_pending; export const statusPageUrl = getMeta('status_page_url'); export const sso_redirect = getMeta('sso_redirect'); diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss index 7adeaeee01..2f4027b03f 100644 --- a/app/javascript/flavours/glitch/styles/admin.scss +++ b/app/javascript/flavours/glitch/styles/admin.scss @@ -143,6 +143,11 @@ $content-width: 840px; } } + .warning a { + color: $gold-star; + font-weight: 700; + } + .simple-navigation-active-leaf a { color: $primary-text-color; background-color: $ui-highlight-color; diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 6c97137964..39b9bd291d 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -977,7 +977,8 @@ $ui-header-height: 55px; } } -.dismissable-banner { +.dismissable-banner, +.warning-banner { position: relative; margin: 10px; margin-bottom: 5px; @@ -1055,6 +1056,21 @@ $ui-header-height: 55px; } } +.warning-banner { + border: 1px solid $warning-red; + background: rgba($warning-red, 0.15); + + &__message { + h1 { + color: $warning-red; + } + + a { + color: $primary-text-color; + } + } +} + .hashtag-header { border-bottom: 1px solid lighten($ui-base-color, 8%); padding: 15px; diff --git a/app/javascript/flavours/glitch/styles/tables.scss b/app/javascript/flavours/glitch/styles/tables.scss index b583d3d8ea..44ef00ba73 100644 --- a/app/javascript/flavours/glitch/styles/tables.scss +++ b/app/javascript/flavours/glitch/styles/tables.scss @@ -12,6 +12,11 @@ border-top: 1px solid $ui-base-color; text-align: start; background: darken($ui-base-color, 4%); + + &.critical { + font-weight: 700; + color: $gold-star; + } } & > thead > tr > th { From d245d6380b6d92bc44c9d03e853dc03ceaac794d Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Sun, 3 Sep 2023 08:46:30 +0200 Subject: [PATCH 084/182] Add `recent` to search props (#2394) The new search popout requires the `recent` prop to be set, otherwise onboarding crashes with "recent is undefined" Signed-off-by: Plastikmensch --- .../flavours/glitch/features/ui/components/onboarding_modal.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx index 15a721ce23..5f93cd2514 100644 --- a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx @@ -78,6 +78,7 @@ const PageThree = ({ myAccount }) => ( onSubmit={noop} onClear={noop} onShow={noop} + recent={{}} />
From e7b2092c4c630dd03cca272f8ebb4b828d8baaee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 09:08:53 +0200 Subject: [PATCH 085/182] Update dependency typescript to v5.2.2 (#26786) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 74ed5214e4..1bd1065355 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8084,6 +8084,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" @@ -8109,6 +8114,11 @@ lodash.invokemap@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz#1748cda5d8b0ef8369c4eb3ec54c21feba1f2d62" integrity sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w== +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -11359,6 +11369,7 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12038,9 +12049,9 @@ typed-array-length@^1.0.4: integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== typescript@^5.0.4: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== unbox-primitive@^1.0.2: version "1.0.2" From 22d4942395870ebad9c75c93f118106c6a6d0dc3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 4 Sep 2023 09:09:25 +0200 Subject: [PATCH 086/182] Fix `before:`, `after:` and `during:` failing when time zone not set (#26782) --- app/lib/search_query_transformer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index e81c0c081e..2c8e95afee 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -95,15 +95,15 @@ class SearchQueryTransformer < Parslet::Transform when 'before' @filter = :created_at @type = :range - @term = { lt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } + @term = { lt: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' } when 'after' @filter = :created_at @type = :range - @term = { gt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } + @term = { gt: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' } when 'during' @filter = :created_at @type = :range - @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } + @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' } else raise "Unknown prefix: #{prefix}" end From f16b40332cee83fd1c8e32672f728c9aaba43dbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 09:26:33 +0200 Subject: [PATCH 087/182] Update DefinitelyTyped types (non-major) (#26785) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1bd1065355..79a03c0e22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,7 +29,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== @@ -44,7 +44,7 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== @@ -2194,9 +2194,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@^29.5.2": - version "29.5.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" - integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== + version "29.5.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2256,9 +2256,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.5.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377" - integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA== + version "20.5.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.9.tgz#a70ec9d8fa0180a314c3ede0e20ea56ff71aed9a" + integrity sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ== "@types/node@14 || 16 || 17": version "17.0.45" @@ -2276,9 +2276,9 @@ integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ== "@types/object-assign@^4.0.30": - version "4.0.30" - resolved "https://registry.yarnpkg.com/@types/object-assign/-/object-assign-4.0.30.tgz#8949371d5a99f4381ee0f1df0a9b7a187e07e652" - integrity sha512-HhE8gFfLj321pa6OE59QmOdL5NgIOhkdYn7MWnZTOcHOms8XFzNgr9+A0/GbN0XEX9wTM58yg4YXKhGr69QIUw== + version "4.0.31" + resolved "https://registry.yarnpkg.com/@types/object-assign/-/object-assign-4.0.31.tgz#9493903157310e28d97b8775b3ce0a7c6373ae46" + integrity sha512-Xw3AoamIJzBgAV2/Rd/TnnpIpkVKeI1IkpOaxy6eIFR55AVnWd5WKqiIkkozamoN/+mSukK/4VgN9zcT7hbOIA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2406,9 +2406,9 @@ "@types/react" "*" "@types/react-test-renderer@^18.0.0": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243" - integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ== + version "18.0.1" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.1.tgz#d3f308912fcc4491e4fbc134b906bb65bdee73f6" + integrity sha512-LjEF+jTUCjzd+Qq4eWqsmZvEWPA/l4L0my+YWN5US8Fo3wZOMiyrpBshHDFbkO8usjdO1B430mEWNU/i1MF7Qg== dependencies: "@types/react" "*" @@ -2526,9 +2526,9 @@ integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== "@types/uuid@^9.0.0": - version "9.0.2" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b" - integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ== + version "9.0.3" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.3.tgz#6cdd939b4316b4f81625de9f06028d848c4a1533" + integrity sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug== "@types/warning@^3.0.0": version "3.0.0" From e51426c1dcf1f9142b24a4ea2f33b5fb8d23d46c Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Mon, 4 Sep 2023 09:46:33 +0200 Subject: [PATCH 088/182] Fix invalid Content-Type header for WebP images (#26773) --- config/initializers/mime_types.rb | 5 +++-- spec/models/media_attachment_spec.rb | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index b843c09ead..dde308cbc1 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true -# Be sure to restart your server when you modify this file. - Mime::Type.register 'application/json', :json, %w(text/x-json application/jsonrequest application/jrd+json application/activity+json application/ld+json) Mime::Type.register 'text/xml', :xml, %w(application/xml application/atom+xml application/xrd+xml) + +# WebP is not defined in Rack 2.2. +Rack::Mime::MIME_TYPES['.webp'] = 'image/webp' diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index d57d43eda7..6a82c8135a 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -105,6 +105,9 @@ RSpec.describe MediaAttachment, paperclip_processing: true do # sets file extension expect(media.file_file_name).to end_with extension + + # Rack::Mime (used by PublicFileServerMiddleware) recognizes file extension + expect(Rack::Mime.mime_type(extension, nil)).to eq content_type end it 'saves media attachment with correct size metadata' do From cfd21eee966b0d30cee371067ea166b4578106fb Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Mon, 4 Sep 2023 10:14:01 +0200 Subject: [PATCH 089/182] Fix light mode colors for advanced interface banner (#26759) --- app/javascript/styles/mastodon/components.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ef2b27d6a8..34c1594e82 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3272,8 +3272,8 @@ $ui-header-height: 55px; } .switch-to-advanced { - color: $classic-primary-color; - background-color: $classic-base-color; + color: $light-text-color; + background-color: $ui-base-color; padding: 15px; border-radius: 4px; margin-top: 4px; From 2c3f3e03904d53b16ab7642e03f5daad8671aef5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 4 Sep 2023 10:18:45 +0200 Subject: [PATCH 090/182] Fix `#hashtag` matching non-hashtagged posts in search (#26781) --- app/chewy/public_statuses_index.rb | 13 ++++++++++++- app/chewy/statuses_index.rb | 13 ++++++++++++- app/chewy/tags_index.rb | 24 +++++++++++++++--------- app/lib/search_query_transformer.rb | 6 +++++- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 5c68a13658..4be204d4a9 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -37,18 +37,29 @@ class PublicStatusesIndex < Chewy::Index english_stemmer ), }, + + hashtag: { + tokenizer: 'keyword', + filter: %w( + word_delimiter_graph + lowercase + asciifolding + cjk_width + ), + }, }, } index_scope ::Status.unscoped .kept .indexable - .includes(:media_attachments, :preloadable_poll, :preview_cards) + .includes(:media_attachments, :preloadable_poll, :preview_cards, :tags) root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:created_at, type: 'date') diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 2be7e45250..6b25dc9dff 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -37,15 +37,26 @@ class StatusesIndex < Chewy::Index english_stemmer ), }, + + hashtag: { + tokenizer: 'keyword', + filter: %w( + word_delimiter_graph + lowercase + asciifolding + cjk_width + ), + }, }, } - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) }) field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb index b2d50a000c..5b6349a964 100644 --- a/app/chewy/tags_index.rb +++ b/app/chewy/tags_index.rb @@ -5,12 +5,21 @@ class TagsIndex < Chewy::Index analyzer: { content: { tokenizer: 'keyword', - filter: %w(lowercase asciifolding cjk_width), + filter: %w( + word_delimiter_graph + lowercase + asciifolding + cjk_width + ), }, edge_ngram: { tokenizer: 'edge_ngram', - filter: %w(lowercase asciifolding cjk_width), + filter: %w( + lowercase + asciifolding + cjk_width + ), }, }, @@ -30,12 +39,9 @@ class TagsIndex < Chewy::Index end root date_detection: false do - field :name, type: 'text', analyzer: 'content' do - field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' - end - - field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } - field :usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts } - field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } + field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') } + field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }) + field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }) + field(:last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }) end end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 2c8e95afee..af3964fd3c 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -53,7 +53,11 @@ class SearchQueryTransformer < Parslet::Transform end def to_query - { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } + if @term.start_with?('#') + { match: { tags: { query: @term } } } + else + { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } + end end end From 536958f4899dc373396a80cbcbd4e08f9b055ddf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:00:55 +0200 Subject: [PATCH 091/182] Update dependency rubocop to v1.56.2 (#26568) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4e30c42222..642be3f9ac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -640,7 +640,7 @@ GEM sidekiq (>= 5, < 8) rspec-support (3.12.1) rspec_chunked (0.6) - rubocop (1.56.1) + rubocop (1.56.2) base64 (~> 0.1.1) json (~> 2.3) language_server-protocol (>= 3.17.0) From 1d954462bb8dd0d122eb18b9cbf523419054c8be Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:02:36 +0200 Subject: [PATCH 092/182] Update dependency stylelint-config-standard-scss to v11 (#26780) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 60 ++++++++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index e92115610b..121abf77fe 100644 --- a/package.json +++ b/package.json @@ -207,7 +207,7 @@ "prettier": "^3.0.0", "react-test-renderer": "^18.2.0", "stylelint": "^15.10.1", - "stylelint-config-standard-scss": "^10.0.0", + "stylelint-config-standard-scss": "^11.0.0", "typescript": "^5.0.4", "webpack-dev-server": "^3.11.3", "yargs": "^17.7.2" diff --git a/yarn.lock b/yarn.lock index 79a03c0e22..a32356ac96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9610,10 +9610,10 @@ postcss-safe-parser@^6.0.0: resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== -postcss-scss@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.6.tgz#5d62a574b950a6ae12f2aa89b60d63d9e4432bfd" - integrity sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ== +postcss-scss@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.7.tgz#cfe5507aaff81b3d8992039ad015da4bd3dccd2f" + integrity sha512-xPv2GseoyXPa58Nro7M73ZntttusuCmZdeOojUFR5PZDz2BR62vfYx1w9TyOnp1+nYFowgOMipsCBhxzVkAEPw== postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.13, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: version "6.0.13" @@ -11459,39 +11459,39 @@ stylehacks@^6.0.0: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -stylelint-config-recommended-scss@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-12.0.0.tgz#9d9e82c46012649f11bfebcbc788f58e61860f33" - integrity sha512-5Bb2mlGy6WLa30oNeKpZvavv2lowJUsUJO25+OA68GFTemlwd1zbFsL7q0bReKipOSU3sG47hKneZ6Nd+ctrFA== +stylelint-config-recommended-scss@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-13.0.0.tgz#dd8c319e15a6412262cd8554e4aad9bfba1bbb11" + integrity sha512-7AmMIsHTsuwUQm7I+DD5BGeIgCvqYZ4BpeYJJpb1cUXQwrJAKjA+GBotFZgUEGP8lAM+wmd91ovzOi8xfAyWEw== dependencies: - postcss-scss "^4.0.6" - stylelint-config-recommended "^12.0.0" - stylelint-scss "^5.0.0" + postcss-scss "^4.0.7" + stylelint-config-recommended "^13.0.0" + stylelint-scss "^5.1.0" -stylelint-config-recommended@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz#d0993232fca017065fd5acfcb52dd8a188784ef4" - integrity sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ== +stylelint-config-recommended@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-13.0.0.tgz#c48a358cc46b629ea01f22db60b351f703e00597" + integrity sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ== -stylelint-config-standard-scss@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-10.0.0.tgz#159a54a01b80649bf0143fa7ba086b676a1a749e" - integrity sha512-bChBEo1p3xUVWh/wenJI+josoMk21f2yuLDGzGjmKYcALfl2u3DFltY+n4UHswYiXghqXaA8mRh+bFy/q1hQlg== +stylelint-config-standard-scss@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-11.0.0.tgz#98332b68a9c98b6fce54c7698741e103719942b5" + integrity sha512-fGE79NBOLg09a9afqGH/guJulRULCaQWWv4cv1v2bMX92B+fGb0y56WqIguwvFcliPmmUXiAhKrrnXilIeXoHA== dependencies: - stylelint-config-recommended-scss "^12.0.0" - stylelint-config-standard "^33.0.0" + stylelint-config-recommended-scss "^13.0.0" + stylelint-config-standard "^34.0.0" -stylelint-config-standard@^33.0.0: - version "33.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz#1f7bb299153a53874073e93829e37a475842f0f9" - integrity sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg== +stylelint-config-standard@^34.0.0: + version "34.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-34.0.0.tgz#309f3c48118a02aae262230c174282e40e766cf4" + integrity sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ== dependencies: - stylelint-config-recommended "^12.0.0" + stylelint-config-recommended "^13.0.0" -stylelint-scss@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.0.1.tgz#b33a6580b5734eace083cfc2cc3021225e28547f" - integrity sha512-n87iCRZrr2J7//I/QFsDXxFLnHKw633U4qvWZ+mOW6KDAp/HLj06H+6+f9zOuTYy+MdGdTuCSDROCpQIhw5fvQ== +stylelint-scss@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.1.0.tgz#dd318bc5c65f7a11f3ecacc7b6e8b67e7f2f1df1" + integrity sha512-E+KlQFXv1Euha43qw3q+wKBSli557wxbbo6/39DWhRNXlUa9Cz+FYrcgz+PT6ag0l6UisCYjAGCNhoSl4FcwlA== dependencies: postcss-media-query-parser "^0.2.3" postcss-resolve-nested-selector "^0.1.1" From 3ae5f3583397e5c27e97da6b600377178740c0bc Mon Sep 17 00:00:00 2001 From: gunchleoc Date: Mon, 4 Sep 2023 13:21:43 +0200 Subject: [PATCH 093/182] Remove kmr from language selection (#26014) --- app/helpers/languages_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb index e76def5818..a8c66552cf 100644 --- a/app/helpers/languages_helper.rb +++ b/app/helpers/languages_helper.rb @@ -193,7 +193,6 @@ module LanguagesHelper cnr: ['Montenegrin', 'crnogorski'].freeze, jbo: ['Lojban', 'la .lojban.'].freeze, kab: ['Kabyle', 'Taqbaylit'].freeze, - kmr: ['Kurmanji (Kurdish)', 'Kurmancî'].freeze, ldn: ['Láadan', 'Láadan'].freeze, lfn: ['Lingua Franca Nova', 'lingua franca nova'].freeze, sco: ['Scots', 'Scots'].freeze, From a93f7392fdd5ff541b6b0872f5a3050f2913518c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 4 Sep 2023 17:20:35 +0200 Subject: [PATCH 094/182] Add `in:library` syntax to search (#26760) Co-authored-by: Claire --- app/lib/search_query_transformer.rb | 88 ++++++++++++++++++++--- app/services/statuses_search_service.rb | 37 +--------- spec/lib/search_query_transformer_spec.rb | 33 ++++----- 3 files changed, 98 insertions(+), 60 deletions(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index af3964fd3c..2dc10830d4 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -9,23 +9,90 @@ class SearchQueryTransformer < Parslet::Transform before after during + in ).freeze class Query - attr_reader :must_not_clauses, :must_clauses, :filter_clauses + def initialize(clauses, options = {}) + raise ArgumentError if options[:current_account].nil? - def initialize(clauses) - grouped = clauses.compact.chunk(&:operator).to_h - @must_not_clauses = grouped.fetch(:must_not, []) - @must_clauses = grouped.fetch(:must, []) - @filter_clauses = grouped.fetch(:filter, []) + @clauses = clauses + @options = options + + flags_from_clauses! end - def apply(search) + def request + search = Chewy::Search::Request.new(*indexes).filter(default_filter) + must_clauses.each { |clause| search = search.query.must(clause.to_query) } must_not_clauses.each { |clause| search = search.query.must_not(clause.to_query) } filter_clauses.each { |clause| search = search.filter(**clause.to_query) } - search.query.minimum_should_match(1) + + search + end + + private + + def clauses_by_operator + @clauses_by_operator ||= @clauses.compact.chunk(&:operator).to_h + end + + def flags_from_clauses! + @flags = clauses_by_operator.fetch(:flag, []).to_h { |clause| [clause.prefix, clause.term] } + end + + def must_clauses + clauses_by_operator.fetch(:must, []) + end + + def must_not_clauses + clauses_by_operator.fetch(:must_not, []) + end + + def filter_clauses + clauses_by_operator.fetch(:filter, []) + end + + def indexes + case @flags['in'] + when 'library' + [StatusesIndex] + else + [PublicStatusesIndex, StatusesIndex] + end + end + + def default_filter + { + bool: { + should: [ + { + term: { + _index: PublicStatusesIndex.index_name, + }, + }, + { + bool: { + must: [ + { + term: { + _index: StatusesIndex.index_name, + }, + }, + { + term: { + searchable_by: @options[:current_account].id, + }, + }, + ], + }, + }, + ], + + minimum_should_match: 1, + }, + } end end @@ -108,6 +175,9 @@ class SearchQueryTransformer < Parslet::Transform @filter = :created_at @type = :range @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' } + when 'in' + @operator = :flag + @term = term else raise "Unknown prefix: #{prefix}" end @@ -176,6 +246,6 @@ class SearchQueryTransformer < Parslet::Transform end rule(query: sequence(:clauses)) do - Query.new(clauses) + Query.new(clauses, current_account: current_account) end end diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 2317a2a1ac..e4b38a9dab 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -14,20 +14,8 @@ class StatusesSearchService < BaseService private def status_search_results - definition = parsed_query.apply( - Chewy::Search::Request.new(StatusesIndex, PublicStatusesIndex).filter( - bool: { - should: [ - publicly_searchable, - non_publicly_searchable, - ], - - minimum_should_match: 1, - } - ) - ) - - results = definition.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact + request = parsed_query.request + results = request.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact account_ids = results.map(&:account_id) account_domains = results.map(&:account_domain) preloaded_relations = @account.relations_map(account_ids, account_domains) @@ -37,27 +25,6 @@ class StatusesSearchService < BaseService [] end - def publicly_searchable - { - term: { _index: PublicStatusesIndex.index_name }, - } - end - - def non_publicly_searchable - { - bool: { - must: [ - { - term: { _index: StatusesIndex.index_name }, - }, - { - term: { searchable_by: @account.id }, - }, - ], - }, - } - end - def parsed_query SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account) end diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 17f06d2833..4b949b1b82 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -3,17 +3,18 @@ require 'rails_helper' describe SearchQueryTransformer do - subject { described_class.new.apply(parser, current_account: nil) } + subject { described_class.new.apply(parser, current_account: account) } + let(:account) { Fabricate(:account) } let(:parser) { SearchQueryParser.new.parse(query) } context 'with "hello world"' do let(:query) { 'hello world' } it 'transforms clauses' do - expect(subject.must_clauses.map(&:term)).to match_array %w(hello world) - expect(subject.must_not_clauses).to be_empty - expect(subject.filter_clauses).to be_empty + expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello world) + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses)).to be_empty end end @@ -21,9 +22,9 @@ describe SearchQueryTransformer do let(:query) { 'hello -world' } it 'transforms clauses' do - expect(subject.must_clauses.map(&:term)).to match_array %w(hello) - expect(subject.must_not_clauses.map(&:term)).to match_array %w(world) - expect(subject.filter_clauses).to be_empty + expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello) + expect(subject.send(:must_not_clauses).map(&:term)).to match_array %w(world) + expect(subject.send(:filter_clauses)).to be_empty end end @@ -31,9 +32,9 @@ describe SearchQueryTransformer do let(:query) { 'hello is:reply' } it 'transforms clauses' do - expect(subject.must_clauses.map(&:term)).to match_array %w(hello) - expect(subject.must_not_clauses).to be_empty - expect(subject.filter_clauses.map(&:term)).to match_array %w(reply) + expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello) + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses).map(&:term)).to match_array %w(reply) end end @@ -41,9 +42,9 @@ describe SearchQueryTransformer do let(:query) { 'foo: bar' } it 'transforms clauses' do - expect(subject.must_clauses.map(&:term)).to match_array %w(foo bar) - expect(subject.must_not_clauses).to be_empty - expect(subject.filter_clauses).to be_empty + expect(subject.send(:must_clauses).map(&:term)).to match_array %w(foo bar) + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses)).to be_empty end end @@ -51,9 +52,9 @@ describe SearchQueryTransformer do let(:query) { 'foo:bar' } it 'transforms clauses' do - expect(subject.must_clauses.map(&:term)).to contain_exactly('foo bar') - expect(subject.must_not_clauses).to be_empty - expect(subject.filter_clauses).to be_empty + expect(subject.send(:must_clauses).map(&:term)).to contain_exactly('foo bar') + expect(subject.send(:must_not_clauses)).to be_empty + expect(subject.send(:filter_clauses)).to be_empty end end end From 49c98615516814d40b006686d64946a88f23ada7 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 4 Sep 2023 17:56:31 +0200 Subject: [PATCH 095/182] Fix language settings for users having selected the `kmr` language (#26787) --- .../20230904134623_fix_kmr_locale_settings.rb | 27 ++++++++++++++++ db/schema.rb | 2 +- lib/tasks/tests.rake | 31 ++++++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 db/post_migrate/20230904134623_fix_kmr_locale_settings.rb diff --git a/db/post_migrate/20230904134623_fix_kmr_locale_settings.rb b/db/post_migrate/20230904134623_fix_kmr_locale_settings.rb new file mode 100644 index 0000000000..10e3f1da99 --- /dev/null +++ b/db/post_migrate/20230904134623_fix_kmr_locale_settings.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class FixKmrLocaleSettings < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + class MigrationUser < ApplicationRecord + self.table_name = :users + end + + def up + MigrationUser.reset_column_information + + MigrationUser.where.not(settings: [nil, '{}']).find_each do |user| + user_settings = Oj.load(user.settings) + next unless user_settings['default_language'] == 'kmr' + + user_settings['default_language'] = 'ku' + user.update!(settings: Oj.dump(user_settings)) + end + + MigrationUser.where.not(chosen_languages: nil).where('chosen_languages && ?', '{kmr}').find_each do |user| + user.update!(chosen_languages: user.chosen_languages.map { |lang| lang == 'kmr' ? 'ku' : lang }.uniq) + end + end + + def down; end +end diff --git a/db/schema.rb b/db/schema.rb index c861069420..21eff86bf7 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[7.0].define(version: 2023_08_22_081029) do +ActiveRecord::Schema[7.0].define(version: 2023_09_04_134623) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake index ef4d46fe09..dbf1405991 100644 --- a/lib/tasks/tests.rake +++ b/lib/tasks/tests.rake @@ -68,10 +68,24 @@ namespace :tests do puts 'Preview cards not deduplicated as expected' exit(1) end + + unless Account.find_local('kmruser').user.chosen_languages == %w(en ku ckb) + puts 'Chosen languages not migrated as expected for kmr users' + exit(1) + end + + unless Account.find_local('kmruser').user.settings['default_language'] == 'ku' + puts 'Default posting language not migrated as expected for kmr users' + exit(1) + end end desc 'Populate the database with test data for 2.4.3' task populate_v2_4_3: :environment do # rubocop:disable Naming/VariableNumber + user_key = OpenSSL::PKey::RSA.new(2048) + user_private_key = ActiveRecord::Base.connection.quote(user_key.to_pem) + user_public_key = ActiveRecord::Base.connection.quote(user_key.public_key.to_pem) + ActiveRecord::Base.connection.execute(<<~SQL) INSERT INTO "custom_filters" (id, account_id, phrase, context, whole_word, irreversible, created_at, updated_at) @@ -118,6 +132,21 @@ namespace :tests do (id, thing_type, thing_id, var, value, created_at, updated_at) VALUES (3, 'User', 1, 'notification_emails', E'--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nfollow: false\nreblog: true\nfavourite: true\nmention: false\nfollow_request: true\ndigest: true\nreport: true\npending_account: false\ntrending_tag: true\nappeal: true\n', now(), now()); + + INSERT INTO "accounts" + (id, username, domain, private_key, public_key, created_at, updated_at) + VALUES + (10, 'kmruser', NULL, #{user_private_key}, #{user_public_key}, now(), now()); + + INSERT INTO "users" + (id, account_id, email, created_at, updated_at, admin, locale, chosen_languages) + VALUES + (4, 10, 'kmruser@localhost', now(), now(), false, 'ku', '{en,kmr,ku,ckb}'); + + INSERT INTO "settings" + (id, thing_type, thing_id, var, value, created_at, updated_at) + VALUES + (4, 'User', 4, 'default_language', E'--- kmr\n', now(), now()); SQL end @@ -197,7 +226,7 @@ namespace :tests do INSERT INTO "users" (id, account_id, email, created_at, updated_at, admin, locale) VALUES - (3, 7, 'ptuser@localhost', now(), now(), false, 'pt'); + (3, 8, 'ptuser@localhost', now(), now(), false, 'pt'); -- conversations INSERT INTO "conversations" (id, created_at, updated_at) VALUES (1, now(), now()); From 45334260181d04ea4893fa3b46255dc609a37302 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 17:56:47 +0200 Subject: [PATCH 096/182] Update babel monorepo to v7.22.15 (#26790) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 389 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 205 insertions(+), 184 deletions(-) diff --git a/yarn.lock b/yarn.lock index a32356ac96..77c589a2ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,7 +29,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.10": +"@babel/code-frame@^7.0.0": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== @@ -44,7 +44,7 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.22.5": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== @@ -58,27 +58,37 @@ integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.10.4", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.11.tgz#8033acaa2aa24c3f814edaaa057f3ce0ba559c24" - integrity sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.15.tgz#15d4fd03f478a459015a4b94cfbb3bd42c48d2f4" + integrity sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-compilation-targets" "^7.22.10" - "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.11" - "@babel/parser" "^7.22.11" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.11" - "@babel/types" "^7.22.11" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.22.15" + "@babel/helpers" "^7.22.15" + "@babel/parser" "^7.22.15" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.22.10", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": +"@babel/generator@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" + integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== + dependencies: + "@babel/types" "^7.22.15" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/generator@^7.22.5", "@babel/generator@^7.7.2": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== @@ -96,11 +106,11 @@ "@babel/types" "^7.22.5" "@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz#573e735937e99ea75ea30788b57eb52fab7468c9" - integrity sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== dependencies: - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.15" "@babel/helper-builder-react-jsx@^7.22.5": version "7.22.5" @@ -110,26 +120,26 @@ "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" - integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" browserslist "^4.21.9" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz#4078686740459eeb4af3494a273ac09148dfb213" - integrity sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ== +"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -137,9 +147,9 @@ semver "^6.3.1" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" - integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" @@ -176,30 +186,37 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" - integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz#b95a144896f6d491ca7863576f820f3628818621" + integrity sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" -"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.5": +"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" - integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== +"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz#40ad2f6950f143900e9c1c72363c0b431a606082" + integrity sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ== dependencies: "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.15" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -257,15 +274,15 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.22.15", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" + integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== "@babel/helper-wrap-function@^7.22.9": version "7.22.10" @@ -276,14 +293,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.10" -"@babel/helpers@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.11.tgz#b02f5d5f2d7abc21ab59eeed80de410ba70b056a" - integrity sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg== +"@babel/helpers@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" + integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.11" - "@babel/types" "^7.22.11" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" "@babel/highlight@^7.22.10", "@babel/highlight@^7.22.13": version "7.22.13" @@ -308,31 +325,26 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== -"@babel/parser@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" - integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== +"@babel/parser@^7.22.15", "@babel/parser@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160" + integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA== -"@babel/parser@^7.22.5": - version "7.22.14" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.14.tgz#c7de58e8de106e88efca42ce17f0033209dfd245" - integrity sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" - integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962" + integrity sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" - integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz#2aeb91d337d4e1a1e7ce85b76a37f5301781200f" + integrity sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.15" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" @@ -494,10 +506,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz#dbe3b1ff5a52e2e5edc4b19a60d325a675ed2649" - integrity sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw== +"@babel/plugin-transform-async-generator-functions@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz#3b153af4a6b779f340d5b80d3f634f55820aefa3" + integrity sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" @@ -520,10 +532,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa" - integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg== +"@babel/plugin-transform-block-scoping@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz#494eb82b87b5f8b1d8f6f28ea74078ec0a10a841" + integrity sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -544,18 +556,18 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" - integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== +"@babel/plugin-transform-classes@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz#aaf4753aee262a232bbc95451b4bdf9599c65a0b" + integrity sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" @@ -567,10 +579,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2" - integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw== +"@babel/plugin-transform-destructuring@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz#e7404ea5bb3387073b9754be654eecb578324694" + integrity sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -613,10 +625,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" - integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== +"@babel/plugin-transform-for-of@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz#f64b4ccc3a4f131a996388fae7680b472b306b29" + integrity sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -667,12 +679,12 @@ "@babel/helper-module-transforms" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz#d7991d3abad199c03b68ee66a64f216c47ffdfae" - integrity sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g== +"@babel/plugin-transform-modules-commonjs@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz#b11810117ed4ee7691b29bd29fd9f3f98276034f" + integrity sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg== dependencies: - "@babel/helper-module-transforms" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" @@ -725,16 +737,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz#dbbb06ce783cd994a8f430d8cefa553e9b42ca62" - integrity sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw== +"@babel/plugin-transform-object-rest-spread@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz#21a95db166be59b91cde48775310c0df6e1da56f" + integrity sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-parameters" "^7.22.15" "@babel/plugin-transform-object-super@^7.22.5": version "7.22.5" @@ -752,19 +764,19 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.12", "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz#d7ebf6a88cd2f4d307b0e000ab630acd8124b333" - integrity sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw== +"@babel/plugin-transform-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz#d7a5996c2f7ca4ad2ad16dbb74444e5c4385b1ba" + integrity sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" - integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== +"@babel/plugin-transform-parameters@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz#719ca82a01d177af358df64a514d64c2e3edb114" + integrity sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -822,16 +834,16 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.22.5" -"@babel/plugin-transform-react-jsx@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" - integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== +"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz#7e6266d88705d7c49f11c98db8b9464531289cd6" + integrity sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" "@babel/plugin-transform-react-pure-annotations@^7.22.5": version "7.22.5" @@ -857,11 +869,11 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-runtime@^7.22.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.10.tgz#89eda6daf1d3af6f36fb368766553054c8d7cd46" - integrity sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz#3a625c4c05a39e932d7d34f5d4895cdd0172fdc9" + integrity sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g== dependencies: - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" @@ -904,13 +916,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typescript@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz#9f27fb5e51585729374bb767ab6a6d9005a23329" - integrity sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA== +"@babel/plugin-transform-typescript@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz#15adef906451d86349eb4b8764865c960eb54127" + integrity sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.11" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -946,16 +958,16 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.22.4": - version "7.22.14" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.14.tgz#1cbb468d899f64fa71c53446f13b7ff8c0005cc1" - integrity sha512-daodMIoVo+ol/g+//c/AH+szBkFj4STQUikvBijRGL72Ph+w+AMTSh55DUETe8KJlPlDT1k/mp7NBfOuiWmoig== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.15.tgz#142716f8e00bc030dae5b2ac6a46fbd8b3e18ff8" + integrity sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.15" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.15" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" @@ -976,39 +988,39 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.11" + "@babel/plugin-transform-async-generator-functions" "^7.22.15" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.10" + "@babel/plugin-transform-block-scoping" "^7.22.15" "@babel/plugin-transform-class-properties" "^7.22.5" "@babel/plugin-transform-class-static-block" "^7.22.11" - "@babel/plugin-transform-classes" "^7.22.6" + "@babel/plugin-transform-classes" "^7.22.15" "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.10" + "@babel/plugin-transform-destructuring" "^7.22.15" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" "@babel/plugin-transform-dynamic-import" "^7.22.11" "@babel/plugin-transform-exponentiation-operator" "^7.22.5" "@babel/plugin-transform-export-namespace-from" "^7.22.11" - "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.15" "@babel/plugin-transform-function-name" "^7.22.5" "@babel/plugin-transform-json-strings" "^7.22.11" "@babel/plugin-transform-literals" "^7.22.5" "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" "@babel/plugin-transform-member-expression-literals" "^7.22.5" "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.11" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" "@babel/plugin-transform-modules-systemjs" "^7.22.11" "@babel/plugin-transform-modules-umd" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.22.5" "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" "@babel/plugin-transform-numeric-separator" "^7.22.11" - "@babel/plugin-transform-object-rest-spread" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.22.15" "@babel/plugin-transform-object-super" "^7.22.5" "@babel/plugin-transform-optional-catch-binding" "^7.22.11" - "@babel/plugin-transform-optional-chaining" "^7.22.12" - "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.15" + "@babel/plugin-transform-parameters" "^7.22.15" "@babel/plugin-transform-private-methods" "^7.22.5" "@babel/plugin-transform-private-property-in-object" "^7.22.11" "@babel/plugin-transform-property-literals" "^7.22.5" @@ -1024,7 +1036,7 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.11" + "@babel/types" "^7.22.15" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" babel-plugin-polyfill-regenerator "^0.5.2" @@ -1041,27 +1053,27 @@ esutils "^2.0.2" "@babel/preset-react@^7.12.5", "@babel/preset-react@^7.22.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.5.tgz#c4d6058fbf80bccad02dd8c313a9aaa67e3c3dd6" - integrity sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.15.tgz#9a776892b648e13cc8ca2edf5ed1264eea6b6afc" + integrity sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" "@babel/plugin-transform-react-display-name" "^7.22.5" - "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/plugin-transform-react-jsx" "^7.22.15" "@babel/plugin-transform-react-jsx-development" "^7.22.5" "@babel/plugin-transform-react-pure-annotations" "^7.22.5" "@babel/preset-typescript@^7.21.5": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz#f218cd0345524ac888aa3dc32f029de5b064b575" - integrity sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.15.tgz#43db30516fae1d417d748105a0bc95f637239d48" + integrity sha512-HblhNmh6yM+cU4VwbBRpxFhxsTdfS1zsvH9W+gEjD0ARV9+8B4sNfpI6GuhePti84nuvhiwKS539jKPFHskA9A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.11" - "@babel/plugin-transform-typescript" "^7.22.11" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-typescript" "^7.22.15" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1076,13 +1088,22 @@ 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.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": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" - integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA== + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" + integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.5", "@babel/template@^7.3.3": +"@babel/template@^7.22.15", "@babel/template@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/template@^7.3.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== @@ -1107,19 +1128,19 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.11.tgz#71ebb3af7a05ff97280b83f05f8865ac94b2027c" - integrity sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ== +"@babel/traverse@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9" + integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ== dependencies: - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.11" - "@babel/types" "^7.22.11" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" debug "^4.1.0" globals "^11.1.0" @@ -1141,13 +1162,13 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5", "@babel/types@^7.4.4": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2" - integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg== +"@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.4.4": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282" + integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA== dependencies: "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.15" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -3901,9 +3922,9 @@ caniuse-lite@^1.0.30001502: integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== caniuse-lite@^1.0.30001517: - version "1.0.30001524" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz#1e14bce4f43c41a7deaeb5ebfe86664fe8dadb80" - integrity sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA== + version "1.0.30001525" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz#d2e8fdec6116ffa36284ca2c33ef6d53612fe1c8" + integrity sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q== caniuse-lite@^1.0.30001520: version "1.0.30001520" @@ -5059,9 +5080,9 @@ electron-to-chromium@^1.4.428: integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== electron-to-chromium@^1.4.477: - version "1.4.505" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.505.tgz#00571ade5975b58413f0f56a665b065bfc29cdfc" - integrity sha512-0A50eL5BCCKdxig2SsCXhpuztnB9PfUgRMojj5tMvt8O54lbwz3t6wNgnpiTRosw5QjlJB7ixhVyeg8daLQwSQ== + version "1.4.508" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz#5641ff2f5ba11df4bd960fe6a2f9f70aa8b9af96" + integrity sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg== elliptic@^6.5.3: version "6.5.4" From 21b72ec6656806cf58e82c9e5f1e2f2960553f0d Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Mon, 4 Sep 2023 10:14:01 +0200 Subject: [PATCH 097/182] [Glitch] Fix light mode colors for advanced interface banner Port cfd21eee966b0d30cee371067ea166b4578106fb to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components/columns.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 39b9bd291d..3135283936 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -229,8 +229,8 @@ $ui-header-height: 55px; } .switch-to-advanced { - color: $classic-primary-color; - background-color: $classic-base-color; + color: $light-text-color; + background-color: $ui-base-color; padding: 15px; border-radius: 4px; margin-top: 4px; From 7b181d2005dcd8fa52f4482359f0178a2d148d0b Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 4 Sep 2023 21:20:20 +0200 Subject: [PATCH 098/182] Bump version to v4.2.0-beta3 (#26753) --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++--------- lib/mastodon/version.rb | 2 +- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe66adc08a..d7cb1ede3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,18 @@ The following changelog entries focus on changes visible to users, administrator ### Added +- **Add full-text search of opted-in public posts and rework search operators** ([Gargron](https://github.com/mastodon/mastodon/pull/26485), [jsgoldstein](https://github.com/mastodon/mastodon/pull/26344), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26657), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26650), [jsgoldstein](https://github.com/mastodon/mastodon/pull/26659), [Gargron](https://github.com/mastodon/mastodon/pull/26660), [Gargron](https://github.com/mastodon/mastodon/pull/26663), [Gargron](https://github.com/mastodon/mastodon/pull/26688), [Gargron](https://github.com/mastodon/mastodon/pull/26689), [Gargron](https://github.com/mastodon/mastodon/pull/26686), [Gargron](https://github.com/mastodon/mastodon/pull/26687), [Gargron](https://github.com/mastodon/mastodon/pull/26692), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26697), [Gargron](https://github.com/mastodon/mastodon/pull/26699), [Gargron](https://github.com/mastodon/mastodon/pull/26701), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26710), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26739), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26754), [Gargron](https://github.com/mastodon/mastodon/pull/26662), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26755), [Gargron](https://github.com/mastodon/mastodon/pull/26781), [Gargron](https://github.com/mastodon/mastodon/pull/26782), [Gargron](https://github.com/mastodon/mastodon/pull/26760)) + This introduces a new `public_statuses` Elasticsearch index for public posts by users who have opted in to their posts being searchable (`toot#indexable` flag). + This also revisits the other indexes to provide more useful indexing, and adds new search operators such as `from:me`, `before:2022-11-01`, `after:2022-11-01`, `during:2022-11-01`, `language:fr`, `has:poll`, or `in:library` (for searching only in posts you have written or interacted with). + Results are now ordered chronologically. +- **Add admin notifications for new Mastodon versions** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26582)) + This is done by querying `https://api.joinmastodon.org/update-check` every 30 minutes in a background job. + That URL can be changed using the `UPDATE_CHECK_URL` environment variable, and the feature outright disabled by setting that variable to an empty string (`UPDATE_CHECK_URL=`). - **Add “Privacy and reach” tab in profile settings** ([Gargron](https://github.com/mastodon/mastodon/pull/26484), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26508)) This reorganized scattered privacy and reach settings to a single place, as well as improve their wording. -- **Add display of out-of-band hashtags in the web interface** ([Gargron](https://github.com/mastodon/mastodon/pull/26492), [arbolitoloco1](https://github.com/mastodon/mastodon/pull/26497), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26506), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26525)) +- **Add display of out-of-band hashtags in the web interface** ([Gargron](https://github.com/mastodon/mastodon/pull/26492), [arbolitoloco1](https://github.com/mastodon/mastodon/pull/26497), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26506), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26525), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26606), [Gargron](https://github.com/mastodon/mastodon/pull/26666)) - **Add role badges to the web interface** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25649), [Gargron](https://github.com/mastodon/mastodon/pull/26281)) -- **Add ability to pick domains to forward reports to using the `forward_to_domains` parameter in `POST /api/v1/reports`** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25866)) +- **Add ability to pick domains to forward reports to using the `forward_to_domains` parameter in `POST /api/v1/reports`** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25866), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26636)) The `forward_to_domains` REST API parameter is a list of strings. If it is empty or omitted, the previous behavior is maintained. The `forward` parameter still needs to be set for `forward_to_domains` to be taken into account. The forwarded-to domains can only include that of the original author and people being replied to. @@ -28,8 +35,15 @@ The following changelog entries focus on changes visible to users, administrator - **Add new onboarding flow to web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24619), [Gargron](https://github.com/mastodon/mastodon/pull/24646), [Gargron](https://github.com/mastodon/mastodon/pull/24705), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24872), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/24883), [Gargron](https://github.com/mastodon/mastodon/pull/24954), [stevenjlm](https://github.com/mastodon/mastodon/pull/24959), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25010), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25275), [Gargron](https://github.com/mastodon/mastodon/pull/25559), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25561)) - **Add `S3_DISABLE_CHECKSUM_MODE` environment variable for compatibility with some S3-compatible providers** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26435)) - **Add auto-refresh of accounts we get new messages/edits of** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26510)) -- **Add Elasticsearch cluster health check and indexes mismatch check to dashboard** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26448)) -- Add support for `indexable` attribute on remote actors ([Gargron](https://github.com/mastodon/mastodon/pull/26485)) +- **Add Elasticsearch cluster health check and indexes mismatch check to dashboard** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26448), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26605), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26658)) +- Add `authorized_fetch` server setting in addition to env var ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25798)) +- Add avatar image to webfinger responses ([tvler](https://github.com/mastodon/mastodon/pull/26558)) +- Add debug logging on signature verification failure ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26637)) +- Add explicit error messages when DeepL quota is exceeded ([lutoma](https://github.com/mastodon/mastodon/pull/26704)) +- Add Elasticsearch/OpenSearch version to “Software” in admin dashboard ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26652)) +- Add `data-nosnippet` attribute to remote posts and local posts with `noindex` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26648)) +- Add support for federating `memorial` attribute ([rrgeorge](https://github.com/mastodon/mastodon/pull/26583)) +- Add Cherokee and Kalmyk to languages dropdown ([gunchleoc](https://github.com/mastodon/mastodon/pull/26012), [gunchleoc](https://github.com/mastodon/mastodon/pull/26013)) - Add `DELETE /api/v1/profile/avatar` and `DELETE /api/v1/profile/header` to the REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25124), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26573)) - Add `ES_PRESET` option to customize numbers of shards and replicas ([Gargron](https://github.com/mastodon/mastodon/pull/26483), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26489)) This can have a value of `single_node_cluster` (default), `small_cluster` (uses one replica) or `large_cluster` (uses one replica and a higher number of shards). @@ -37,13 +51,13 @@ The following changelog entries focus on changes visible to users, administrator - Add `CACHE_BUSTER_HTTP_METHOD` environment variable ([renchap](https://github.com/mastodon/mastodon/pull/26528), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26542)) - Add support for `DB_PASS` when using `DATABASE_URL` ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26295)) - Add `GET /api/v1/instance/languages` to REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24443)) -- Add primary key to `preview_cards_statuses` join table ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25243), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26384), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26447)) +- Add primary key to `preview_cards_statuses` join table ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25243), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26384), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26447), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26737)) - Add client-side timeout on resend confirmation button ([Gargron](https://github.com/mastodon/mastodon/pull/26300)) - Add published date and author to news on the explore screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26155)) - Add `lang` attribute to various UI components ([c960657](https://github.com/mastodon/mastodon/pull/23869), [c960657](https://github.com/mastodon/mastodon/pull/23891), [c960657](https://github.com/mastodon/mastodon/pull/26111), [c960657](https://github.com/mastodon/mastodon/pull/26149)) - Add stricter protocol fields validation for accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25937)) - Add support for Azure blob storage ([mistydemeo](https://github.com/mastodon/mastodon/pull/23607), [mistydemeo](https://github.com/mastodon/mastodon/pull/26080)) -- Add toast with option to open post after publishing in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25564), [Signez](https://github.com/mastodon/mastodon/pull/25919)) +- Add toast with option to open post after publishing in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25564), [Signez](https://github.com/mastodon/mastodon/pull/25919), [Gargron](https://github.com/mastodon/mastodon/pull/26664)) - Add canonical link tags in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25715)) - Add button to see results for polls in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25726)) - Add at-symbol prepended to mention span title ([forsamori](https://github.com/mastodon/mastodon/pull/25684)) @@ -93,15 +107,15 @@ The following changelog entries focus on changes visible to users, administrator ### Changed -- **Change hashtags to be displayed separately when they are the last line of a post** ([renchap](https://github.com/mastodon/mastodon/pull/26499)) +- **Change hashtags to be displayed separately when they are the last line of a post** ([renchap](https://github.com/mastodon/mastodon/pull/26499), [renchap](https://github.com/mastodon/mastodon/pull/26614), [renchap](https://github.com/mastodon/mastodon/pull/26615)) - **Change reblogs to be excluded from "Posts and replies" tab in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26302)) -- **Change interaction modal in web interface** ([Gargron, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26075), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26268), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26267), [mgmn](https://github.com/mastodon/mastodon/pull/26459)) +- **Change interaction modal in web interface** ([Gargron, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26075), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26268), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26267), [mgmn](https://github.com/mastodon/mastodon/pull/26459), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26593)) - **Change design of link previews in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26136), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26151), [Gargron](https://github.com/mastodon/mastodon/pull/26153), [Gargron](https://github.com/mastodon/mastodon/pull/26250), [Gargron](https://github.com/mastodon/mastodon/pull/26287), [Gargron](https://github.com/mastodon/mastodon/pull/26286), [c960657](https://github.com/mastodon/mastodon/pull/26184)) - **Change "direct message" nomenclature to "private mention" in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24248)) - **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26452)) - **Change account search to match by text when opted-in** ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25599), [Gargron](https://github.com/mastodon/mastodon/pull/26378)) - **Change import feature to be clearer, less error-prone and more reliable** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21054), [mgmn](https://github.com/mastodon/mastodon/pull/24874)) -- **Change local and federated timelines to be tabs of a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247)) +- **Change local and federated timelines to be tabs of a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26633)) - **Change user archive export to be faster and more reliable, and export `.zip` archives instead of `.tar.gz` ones** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23360), [TheEssem](https://github.com/mastodon/mastodon/pull/25034)) - **Change `mastodon-streaming` systemd unit files to be templated** ([e-nomem](https://github.com/mastodon/mastodon/pull/24751)) - **Change `statsd` integration to disable sidekiq metrics by default** ([mjankowski](https://github.com/mastodon/mastodon/pull/25265), [mjankowski](https://github.com/mastodon/mastodon/pull/25336), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26310)) @@ -111,6 +125,12 @@ The following changelog entries focus on changes visible to users, administrator - **Change replica support to native Rails adapter** ([krainboltgreene](https://github.com/mastodon/mastodon/pull/25693), [Gargron](https://github.com/mastodon/mastodon/pull/25849), [Gargron](https://github.com/mastodon/mastodon/pull/25874), [Gargron](https://github.com/mastodon/mastodon/pull/25851), [Gargron](https://github.com/mastodon/mastodon/pull/25977), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26074), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26386)) This is a breaking change, dropping `makara` support, and requiring you to update your database configuration if you are using replicas. To tell Mastodon to use a read replica, you can either set the `REPLICA_DB_NAME` environment variable (along with `REPLICA_DB_USER`, `REPLICA_DB_PASS`, `REPLICA_DB_HOST`, and `REPLICA_DB_PORT`, if they differ from the primary database), or the `REPLICA_DATABASE_URL` environment variable if your configuration is based on `DATABASE_URL`. +- Change from `node-redis` to `ioredis` for streaming ([gmemstr](https://github.com/mastodon/mastodon/pull/26581)) +- Change private statuses index to index without crutches ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26713)) +- Change video compression parameters ([Gargron](https://github.com/mastodon/mastodon/pull/26631), [Gargron](https://github.com/mastodon/mastodon/pull/26745), [Gargron](https://github.com/mastodon/mastodon/pull/26766)) +- Change admin e-mail notification settings to be their own settings group ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26596)) +- Change opacity of the delete icon in the search field to be more visible ([AntoninDelFabbro](https://github.com/mastodon/mastodon/pull/26449)) +- Change Account Search to prioritize username over display name ([jsgoldstein](https://github.com/mastodon/mastodon/pull/26623)) - Change follow recommendation materialized view to be faster in most cases ([renchap, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26545)) - Change `robots.txt` to block GPTBot ([Foritus](https://github.com/mastodon/mastodon/pull/26396)) - Change header of hashtag timelines in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26362), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26416)) @@ -120,9 +140,9 @@ The following changelog entries focus on changes visible to users, administrator - Change poll form element colors to fit with the rest of the ui ([teeerevor](https://github.com/mastodon/mastodon/pull/26139), [teeerevor](https://github.com/mastodon/mastodon/pull/26162), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26164)) - Change 'favourite' to 'favorite' for American English ([marekr](https://github.com/mastodon/mastodon/pull/24667), [gunchleoc](https://github.com/mastodon/mastodon/pull/26009), [nabijaczleweli](https://github.com/mastodon/mastodon/pull/26109)) - Change ActivityStreams representation of suspended accounts to not use a blank `name` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25276)) -- Change focus UI for keyboard only input ([teeerevor](https://github.com/mastodon/mastodon/pull/25935), [Gargron](https://github.com/mastodon/mastodon/pull/26125)) +- Change focus UI for keyboard only input ([teeerevor](https://github.com/mastodon/mastodon/pull/25935), [Gargron](https://github.com/mastodon/mastodon/pull/26125), [Gargron](https://github.com/mastodon/mastodon/pull/26767)) - Change thread view to scroll to the selected post rather than the post being replied to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24685)) -- Change links in multi-column mode so tabs are open in single-column mode ([Signez](https://github.com/mastodon/mastodon/pull/25893), [Signez](https://github.com/mastodon/mastodon/pull/26070), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25973)) +- Change links in multi-column mode so tabs are open in single-column mode ([Signez](https://github.com/mastodon/mastodon/pull/25893), [Signez](https://github.com/mastodon/mastodon/pull/26070), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25973), [Signez](https://github.com/mastodon/mastodon/pull/26019), [Signez](https://github.com/mastodon/mastodon/pull/26759)) - Change searching with `#` to include account index ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25638)) - Change label and design of sensitive and unavailable media in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25712), [Gargron](https://github.com/mastodon/mastodon/pull/26135), [Gargron](https://github.com/mastodon/mastodon/pull/26330)) - Change button colors to increase hover/focus contrast and consistency ([teeerevor](https://github.com/mastodon/mastodon/pull/25677), [Gargron](https://github.com/mastodon/mastodon/pull/25679)) @@ -175,6 +195,7 @@ The following changelog entries focus on changes visible to users, administrator - **Remove support for Ruby 2.7** ([nschonni](https://github.com/mastodon/mastodon/pull/24237)) - **Remove clustering from streaming API** ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/24655)) - **Remove anonymous access to the streaming API** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23989)) +- Remove `kmr` from language selection, as it was a duplicate for `ku` ([gunchleoc](https://github.com/mastodon/mastodon/pull/26014), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26787)) - Remove 16:9 cropping from web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26132)) - Remove back button from bookmarks, favourites and lists screens in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26126)) - Remove display name input from sign-up form ([Gargron](https://github.com/mastodon/mastodon/pull/24704)) @@ -190,6 +211,17 @@ The following changelog entries focus on changes visible to users, administrator - **Fix broken links in account gallery** ([c960657](https://github.com/mastodon/mastodon/pull/24218)) - **Fix blocking subdomains of an already-blocked domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26392)) - **Fix migration handler not updating lists** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24808)) +- Fix invalid `Content-Type` header for WebP images ([c960657](https://github.com/mastodon/mastodon/pull/26773)) +- Fix minor inefficiencies in `tootctl search deploy` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26721)) +- Fix filter form in profiles directory overflowing instead of wrapping ([arbolitoloco1](https://github.com/mastodon/mastodon/pull/26682)) +- Fix `/api/v1/timelines/tag/:hashtag` allowing for unauthenticated access when public preview is disabled ([danielmbrasil](https://github.com/mastodon/mastodon/pull/26237)) +- Fix inefficiencies in `PlainTextFormatter` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26727)) +- Fix sign up steps progress layout in right-to-left locales ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26728)) +- Fix bug with “favorited by” and “reblogged by“ view on posts only showing up to 40 items ([timothyjrogers](https://github.com/mastodon/mastodon/pull/26577), [timothyjrogers](https://github.com/mastodon/mastodon/pull/26574)) +- Fix bad search type heuristic ([Gargron](https://github.com/mastodon/mastodon/pull/26673)) +- Fix not being able to negate prefix clauses in search ([Gargron](https://github.com/mastodon/mastodon/pull/26672)) +- Fix timeout on invalid set of exclusionary parameters in `/api/v1/timelines/public` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/26239)) +- Fix unexpected audio stream transcoding when uploaded video is eligible to passthrough ([yufushiro](https://github.com/mastodon/mastodon/pull/26608)) - Fix uploading of video files for which `ffprobe` reports `0/0` average framerate ([NicolaiSoeborg](https://github.com/mastodon/mastodon/pull/26500)) - Fix cached posts including stale stats ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26409)) - Fix adding column with default value taking longer on Postgres >= 11 ([Gargron](https://github.com/mastodon/mastodon/pull/26375)) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 65f90f93fd..8f9760b8e8 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def default_prerelease - 'beta2' + 'beta3' end def prerelease From 96f51d6c1fd4fac12cd29d3b199fac88a4741742 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 5 Sep 2023 15:37:23 +0200 Subject: [PATCH 099/182] =?UTF-8?q?Fix=20=E2=80=9CScoped=20order=20is=20ig?= =?UTF-8?q?nored,=20it's=20forced=20to=20be=20batch=20order.=E2=80=9D=20wa?= =?UTF-8?q?rnings=20(#26793)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/feed_manager.rb | 2 +- app/lib/importer/public_statuses_index_importer.rb | 2 +- app/lib/importer/statuses_index_importer.rb | 2 +- app/models/admin/status_batch_action.rb | 2 +- app/models/concerns/account_merging.rb | 4 ++-- app/models/concerns/account_statuses_search.rb | 2 +- app/models/trends/statuses.rb | 4 ++-- app/services/bulk_import_service.rb | 6 +++--- app/services/import_service.rb | 2 +- app/services/suspend_account_service.rb | 6 +++--- app/services/unsuspend_account_service.rb | 6 +++--- app/workers/move_worker.rb | 2 +- config/environments/test.rb | 3 +++ .../20221101190723_backfill_admin_action_logs.rb | 2 +- .../20221206114142_backfill_admin_action_logs_again.rb | 2 +- 15 files changed, 25 insertions(+), 22 deletions(-) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index ad686c1f1a..26e5b78e8f 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -262,7 +262,7 @@ class FeedManager add_to_feed(:home, account.id, status, aggregate_reblogs: aggregate) end - account.following.includes(:account_stat).find_each do |target_account| + account.following.includes(:account_stat).reorder(nil).find_each do |target_account| if redis.zcard(timeline_key) >= limit oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at) diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb index ebaac3794f..7dfe998868 100644 --- a/app/lib/importer/public_statuses_index_importer.rb +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -27,6 +27,6 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter end def scope - Status.indexable + Status.indexable.reorder(nil) end end diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index 08ad3e3797..1922f65f6d 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -11,7 +11,7 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter # from a different scope to avoid indexing them multiple times, but that # could end up being a very large array - scope.find_in_batches(batch_size: @batch_size) do |tmp| + scope.reorder(nil).find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| deleted = 0 diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb index b8bdec7223..2bf49a7f48 100644 --- a/app/models/admin/status_batch_action.rb +++ b/app/models/admin/status_batch_action.rb @@ -22,7 +22,7 @@ class Admin::StatusBatchAction private def statuses - Status.with_discarded.where(id: status_ids) + Status.with_discarded.where(id: status_ids).reorder(nil) end def process_action! diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb index 41071633db..14e157a3d8 100644 --- a/app/models/concerns/account_merging.rb +++ b/app/models/concerns/account_merging.rb @@ -20,7 +20,7 @@ module AccountMerging ] owned_classes.each do |klass| - klass.where(account_id: other_account.id).find_each do |record| + klass.where(account_id: other_account.id).reorder(nil).find_each do |record| record.update_attribute(:account_id, id) rescue ActiveRecord::RecordNotUnique next @@ -33,7 +33,7 @@ module AccountMerging ] target_classes.each do |klass| - klass.where(target_account_id: other_account.id).find_each do |record| + klass.where(target_account_id: other_account.id).reorder(nil).find_each do |record| record.update_attribute(:target_account_id, id) rescue ActiveRecord::RecordNotUnique next diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb index fa9238e6ef..4b2bc4117a 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account_statuses_search.rb @@ -31,7 +31,7 @@ module AccountStatusesSearch def add_to_public_statuses_index! return unless Chewy.enabled? - statuses.without_reblogs.where(visibility: :public).find_in_batches do |batch| + statuses.without_reblogs.where(visibility: :public).reorder(nil).find_in_batches do |batch| PublicStatusesIndex.import(batch) end end diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb index 5cd352a6f2..886a385a71 100644 --- a/app/models/trends/statuses.rb +++ b/app/models/trends/statuses.rb @@ -62,13 +62,13 @@ class Trends::Statuses < Trends::Base def refresh(at_time = Time.now.utc) # First, recalculate scores for statuses that were trending previously. We split the queries # to avoid having to load all of the IDs into Ruby just to send them back into Postgres - Status.where(id: StatusTrend.select(:status_id)).includes(:status_stat, :account).find_in_batches(batch_size: BATCH_SIZE) do |statuses| + Status.where(id: StatusTrend.select(:status_id)).includes(:status_stat, :account).reorder(nil).find_in_batches(batch_size: BATCH_SIZE) do |statuses| calculate_scores(statuses, at_time) end # Then, calculate scores for statuses that were used today. There are potentially some # duplicate items here that we might process one more time, but that should be fine - Status.where(id: recently_used_ids(at_time)).includes(:status_stat, :account).find_in_batches(batch_size: BATCH_SIZE) do |statuses| + Status.where(id: recently_used_ids(at_time)).includes(:status_stat, :account).reorder(nil).find_in_batches(batch_size: BATCH_SIZE) do |statuses| calculate_scores(statuses, at_time) end diff --git a/app/services/bulk_import_service.rb b/app/services/bulk_import_service.rb index 591b112127..a361c7a3da 100644 --- a/app/services/bulk_import_service.rb +++ b/app/services/bulk_import_service.rb @@ -38,7 +38,7 @@ class BulkImportService < BaseService rows_by_acct = extract_rows_by_acct if @import.overwrite? - @account.following.find_each do |followee| + @account.following.reorder(nil).find_each do |followee| row = rows_by_acct.delete(followee.acct) if row.nil? @@ -67,7 +67,7 @@ class BulkImportService < BaseService rows_by_acct = extract_rows_by_acct if @import.overwrite? - @account.blocking.find_each do |blocked_account| + @account.blocking.reorder(nil).find_each do |blocked_account| row = rows_by_acct.delete(blocked_account.acct) if row.nil? @@ -93,7 +93,7 @@ class BulkImportService < BaseService rows_by_acct = extract_rows_by_acct if @import.overwrite? - @account.muting.find_each do |muted_account| + @account.muting.reorder(nil).find_each do |muted_account| row = rows_by_acct.delete(muted_account.acct) if row.nil? diff --git a/app/services/import_service.rb b/app/services/import_service.rb index 133c081be5..6dafb5a0bb 100644 --- a/app/services/import_service.rb +++ b/app/services/import_service.rb @@ -75,7 +75,7 @@ class ImportService < BaseService if @import.overwrite? presence_hash = items.each_with_object({}) { |(id, extra), mapping| mapping[id] = [true, extra] } - overwrite_scope.find_each do |target_account| + overwrite_scope.reorder(nil).find_each do |target_account| if presence_hash[target_account.acct] items.delete(target_account.acct) extra = presence_hash[target_account.acct][1] diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 9f21fb79bf..e79c2d3d81 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -51,13 +51,13 @@ class SuspendAccountService < BaseService end def unmerge_from_home_timelines! - @account.followers_for_local_distribution.find_each do |follower| + @account.followers_for_local_distribution.reorder(nil).find_each do |follower| FeedManager.instance.unmerge_from_home(@account, follower) end end def unmerge_from_list_timelines! - @account.lists_for_local_distribution.find_each do |list| + @account.lists_for_local_distribution.reorder(nil).find_each do |list| FeedManager.instance.unmerge_from_list(@account, list) end end @@ -65,7 +65,7 @@ class SuspendAccountService < BaseService def privatize_media_attachments! attachment_names = MediaAttachment.attachment_definitions.keys - @account.media_attachments.find_each do |media_attachment| + @account.media_attachments.reorder(nil).find_each do |media_attachment| attachment_names.each do |attachment_name| attachment = media_attachment.public_send(attachment_name) styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb index e555932f81..93cd04a943 100644 --- a/app/services/unsuspend_account_service.rb +++ b/app/services/unsuspend_account_service.rb @@ -47,13 +47,13 @@ class UnsuspendAccountService < BaseService end def merge_into_home_timelines! - @account.followers_for_local_distribution.find_each do |follower| + @account.followers_for_local_distribution.reorder(nil).find_each do |follower| FeedManager.instance.merge_into_home(@account, follower) end end def merge_into_list_timelines! - @account.lists_for_local_distribution.find_each do |list| + @account.lists_for_local_distribution.reorder(nil).find_each do |list| FeedManager.instance.merge_into_list(@account, list) end end @@ -61,7 +61,7 @@ class UnsuspendAccountService < BaseService def publish_media_attachments! attachment_names = MediaAttachment.attachment_definitions.keys - @account.media_attachments.find_each do |media_attachment| + @account.media_attachments.reorder(nil).find_each do |media_attachment| attachment_names.each do |attachment_name| attachment = media_attachment.public_send(attachment_name) styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb index cb091671df..73ae268bee 100644 --- a/app/workers/move_worker.rb +++ b/app/workers/move_worker.rb @@ -72,7 +72,7 @@ class MoveWorker def queue_follow_unfollows! bypass_locked = @target_account.local? - @source_account.followers.local.select(:id).find_in_batches do |accounts| + @source_account.followers.local.select(:id).reorder(nil).find_in_batches do |accounts| UnfollowFollowWorker.push_bulk(accounts.map(&:id)) { |follower_id| [follower_id, @source_account.id, @target_account.id, bypass_locked] } rescue => e @deferred_error = e diff --git a/config/environments/test.rb b/config/environments/test.rb index d90dca429e..210329848e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -50,6 +50,9 @@ Rails.application.configure do config.x.vapid_private_key = vapid_key.private_key config.x.vapid_public_key = vapid_key.public_key + # Raise exceptions when a reorder occurs in in_batches + config.active_record.error_on_ignored_order = true + # Raise exceptions for disallowed deprecations. config.active_support.disallowed_deprecation = :raise diff --git a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb index fa2ddbbca5..6476f4b13a 100644 --- a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb +++ b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb @@ -90,7 +90,7 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1] log.update_attribute('route_param', log.user.account_id) end - Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| next if log.domain_block.nil? diff --git a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb index 9c7ac7120a..3c68470a7f 100644 --- a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb +++ b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb @@ -90,7 +90,7 @@ class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1] log.update_attribute('route_param', log.user.account_id) end - Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| next if log.domain_block.nil? From 773e2d44f2d1fce5fdd0dc8322d453decbcfbe3c Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 5 Sep 2023 11:19:59 -0500 Subject: [PATCH 100/182] Migrate Dockerfile to Bookworm (#26802) --- Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index cdabc4c7c8..b22284bbd1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.4 -# This needs to be bullseye-slim because the Ruby image is built on bullseye-slim -ARG NODE_VERSION="16.20-bullseye-slim" +# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim +ARG NODE_VERSION="16.20-bookworm-slim" FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby FROM node:${NODE_VERSION} as build @@ -20,7 +20,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends build-essential \ git \ libicu-dev \ - libidn11-dev \ + libidn-dev \ libpq-dev \ libjemalloc-dev \ zlib1g-dev \ @@ -64,13 +64,13 @@ RUN apt-get update && \ apt-get -y --no-install-recommends install whois \ wget \ procps \ - libssl1.1 \ + libssl3 \ libpq5 \ imagemagick \ ffmpeg \ libjemalloc2 \ - libicu67 \ - libidn11 \ + libicu72 \ + libidn12 \ libyaml-0-2 \ file \ ca-certificates \ From 529ee44ff6fe92fec6a21a316cae79b8d5dae5e6 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 5 Sep 2023 20:05:58 +0200 Subject: [PATCH 101/182] Fix video player not being displayed in reports interface (#26801) --- app/helpers/media_component_helper.rb | 1 + app/views/admin/reports/_media_attachments.html.haml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/media_component_helper.rb b/app/helpers/media_component_helper.rb index a57d0b4b62..fa8f34fb4d 100644 --- a/app/helpers/media_component_helper.rb +++ b/app/helpers/media_component_helper.rb @@ -14,6 +14,7 @@ module MediaComponentHelper blurhash: video.blurhash, frameRate: meta.dig('original', 'frame_rate'), inline: true, + aspectRatio: "#{meta.dig('original', 'width')} / #{meta.dig('original', 'height')}", media: [ serialize_media_attachment(video), ].as_json, diff --git a/app/views/admin/reports/_media_attachments.html.haml b/app/views/admin/reports/_media_attachments.html.haml index 2305805a75..8ecd7444d2 100644 --- a/app/views/admin/reports/_media_attachments.html.haml +++ b/app/views/admin/reports/_media_attachments.html.haml @@ -1,6 +1,5 @@ - if status.ordered_media_attachments.first.video? - - video = status.ordered_media_attachments.first - = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), frameRate: video.file.meta.dig('original', 'frame_rate'), blurhash: video.blurhash, sensitive: status.sensitive?, visible: false, width: 610, height: 343, inline: true, alt: video.description, lang: status.language, media: [ActiveModelSerializers::SerializableResource.new(video, serializer: REST::MediaAttachmentSerializer)].as_json + = render_video_component(status, visible: false) - elsif status.ordered_media_attachments.first.audio? - audio = status.ordered_media_attachments.first = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, lang: status.language, duration: audio.file.meta.dig(:original, :duration) From a8a25dcbdb63efb3191dbb3720c385209ffe4f34 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 5 Sep 2023 23:49:48 +0200 Subject: [PATCH 102/182] Improve interaction modal error handling (#26795) --- .../api/v1/peers/search_controller.rb | 2 + .../features/interaction_modal/index.jsx | 52 ++++++++++++++++--- .../packs/remote_interaction_helper.ts | 4 +- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/v1/peers/search_controller.rb b/app/controllers/api/v1/peers/search_controller.rb index 2c0eacdcae..0c503d9bc5 100644 --- a/app/controllers/api/v1/peers/search_controller.rb +++ b/app/controllers/api/v1/peers/search_controller.rb @@ -41,5 +41,7 @@ class Api::V1::Peers::SearchController < Api::BaseController domain = TagManager.instance.normalize_domain(domain) @domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain) end + rescue Addressable::URI::InvalidURIError + @domains = [] end end diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx index 84e4309254..2a9fa0e33e 100644 --- a/app/javascript/mastodon/features/interaction_modal/index.jsx +++ b/app/javascript/mastodon/features/interaction_modal/index.jsx @@ -100,8 +100,41 @@ class LoginForm extends React.PureComponent { this.input = c; }; + isValueValid = (value) => { + let likelyAcct = false; + let url = null; + + if (value.startsWith('/')) { + return false; + } + + if (value.startsWith('@')) { + value = value.slice(1); + likelyAcct = true; + } + + // The user is in the middle of typing something, do not error out + if (value === '') { + return true; + } + + if (/^https?:\/\//.test(value) && !likelyAcct) { + url = value; + } else { + url = `https://${value}`; + } + + try { + new URL(url); + return true; + } catch(_) { + return false; + } + }; + handleChange = ({ target }) => { - this.setState(state => ({ value: target.value, isLoading: true, error: false, options: addInputToOptions(target.value, state.networkOptions) }), () => this._loadOptions()); + const error = !this.isValueValid(target.value); + this.setState(state => ({ error, value: target.value, isLoading: true, options: addInputToOptions(target.value, state.networkOptions) }), () => this._loadOptions()); }; handleMessage = (event) => { @@ -115,11 +148,18 @@ class LoginForm extends React.PureComponent { this.setState({ isSubmitting: false, error: true }); } else if (event.data?.type === 'fetchInteractionURL-success') { if (/^https?:\/\//.test(event.data.template)) { - if (localStorage) { - localStorage.setItem(PERSISTENCE_KEY, event.data.uri_or_domain); - } + try { + const url = new URL(event.data.template.replace('{uri}', encodeURIComponent(resourceUrl))); - window.location.href = event.data.template.replace('{uri}', encodeURIComponent(resourceUrl)); + if (localStorage) { + localStorage.setItem(PERSISTENCE_KEY, event.data.uri_or_domain); + } + + window.location.href = url; + } catch (e) { + console.error(e); + this.setState({ isSubmitting: false, error: true }); + } } else { this.setState({ isSubmitting: false, error: true }); } @@ -259,7 +299,7 @@ class LoginForm extends React.PureComponent { spellcheck='false' /> - +
{hasPopOut && ( diff --git a/app/javascript/packs/remote_interaction_helper.ts b/app/javascript/packs/remote_interaction_helper.ts index 76528ff381..d5834c6c3d 100644 --- a/app/javascript/packs/remote_interaction_helper.ts +++ b/app/javascript/packs/remote_interaction_helper.ts @@ -140,7 +140,9 @@ const fromAcct = (acct: string) => { }; const fetchInteractionURL = (uri_or_domain: string) => { - if (/^https?:\/\//.test(uri_or_domain)) { + if (uri_or_domain === '') { + fetchInteractionURLFailure(); + } else if (/^https?:\/\//.test(uri_or_domain)) { fromURL(uri_or_domain); } else if (uri_or_domain.includes('@')) { fromAcct(uri_or_domain); From 01e3a1f941727282d1eaa05cdfe3902b6933f8dc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 5 Sep 2023 23:54:24 +0200 Subject: [PATCH 103/182] Add infinite scrolling for search results in web UI (#26784) --- app/javascript/mastodon/actions/search.js | 22 ++- .../compose/components/search_results.jsx | 121 ++++-------- .../explore/components/search_section.jsx | 20 ++ .../mastodon/features/explore/results.jsx | 179 ++++++++++++++---- app/javascript/mastodon/locales/en.json | 5 +- app/javascript/mastodon/reducers/search.js | 20 +- .../styles/mastodon/components.scss | 59 +++--- 7 files changed, 255 insertions(+), 171 deletions(-) create mode 100644 app/javascript/mastodon/features/explore/components/search_section.jsx diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js index 94e7f2ed75..21fd540768 100644 --- a/app/javascript/mastodon/actions/search.js +++ b/app/javascript/mastodon/actions/search.js @@ -37,17 +37,17 @@ export function submitSearch(type) { const signedIn = !!getState().getIn(['meta', 'me']); if (value.length === 0) { - dispatch(fetchSearchSuccess({ accounts: [], statuses: [], hashtags: [] }, '')); + dispatch(fetchSearchSuccess({ accounts: [], statuses: [], hashtags: [] }, '', type)); return; } - dispatch(fetchSearchRequest()); + dispatch(fetchSearchRequest(type)); api(getState).get('/api/v2/search', { params: { q: value, resolve: signedIn, - limit: 5, + limit: 11, type, }, }).then(response => { @@ -59,7 +59,7 @@ export function submitSearch(type) { dispatch(importFetchedStatuses(response.data.statuses)); } - dispatch(fetchSearchSuccess(response.data, value)); + dispatch(fetchSearchSuccess(response.data, value, type)); dispatch(fetchRelationships(response.data.accounts.map(item => item.id))); }).catch(error => { dispatch(fetchSearchFail(error)); @@ -67,16 +67,18 @@ export function submitSearch(type) { }; } -export function fetchSearchRequest() { +export function fetchSearchRequest(searchType) { return { type: SEARCH_FETCH_REQUEST, + searchType, }; } -export function fetchSearchSuccess(results, searchTerm) { +export function fetchSearchSuccess(results, searchTerm, searchType) { return { type: SEARCH_FETCH_SUCCESS, results, + searchType, searchTerm, }; } @@ -90,15 +92,16 @@ export function fetchSearchFail(error) { export const expandSearch = type => (dispatch, getState) => { const value = getState().getIn(['search', 'value']); - const offset = getState().getIn(['search', 'results', type]).size; + const offset = getState().getIn(['search', 'results', type]).size - 1; - dispatch(expandSearchRequest()); + dispatch(expandSearchRequest(type)); api(getState).get('/api/v2/search', { params: { q: value, type, offset, + limit: 11, }, }).then(({ data }) => { if (data.accounts) { @@ -116,8 +119,9 @@ export const expandSearch = type => (dispatch, getState) => { }); }; -export const expandSearchRequest = () => ({ +export const expandSearchRequest = (searchType) => ({ type: SEARCH_EXPAND_REQUEST, + searchType, }); export const expandSearchSuccess = (results, searchTerm, searchType) => ({ diff --git a/app/javascript/mastodon/features/compose/components/search_results.jsx b/app/javascript/mastodon/features/compose/components/search_results.jsx index b11ac478a4..346d9b18aa 100644 --- a/app/javascript/mastodon/features/compose/components/search_results.jsx +++ b/app/javascript/mastodon/features/compose/components/search_results.jsx @@ -1,46 +1,36 @@ import PropTypes from 'prop-types'; -import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { Icon } from 'mastodon/components/icon'; import { LoadMore } from 'mastodon/components/load_more'; +import { SearchSection } from 'mastodon/features/explore/components/search_section'; import { ImmutableHashtag as Hashtag } from '../../../components/hashtag'; import AccountContainer from '../../../containers/account_container'; import StatusContainer from '../../../containers/status_container'; -import { searchEnabled } from '../../../initial_state'; -const messages = defineMessages({ - dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, -}); +const INITIAL_PAGE_LIMIT = 10; + +const withoutLastResult = list => { + if (list.size > INITIAL_PAGE_LIMIT && list.size % INITIAL_PAGE_LIMIT === 1) { + return list.skipLast(1); + } else { + return list; + } +}; class SearchResults extends ImmutablePureComponent { static propTypes = { results: ImmutablePropTypes.map.isRequired, - suggestions: ImmutablePropTypes.list.isRequired, - fetchSuggestions: PropTypes.func.isRequired, expandSearch: PropTypes.func.isRequired, - dismissSuggestion: PropTypes.func.isRequired, searchTerm: PropTypes.string, - intl: PropTypes.object.isRequired, }; - componentDidMount () { - if (this.props.searchTerm === '') { - this.props.fetchSuggestions(); - } - } - - componentDidUpdate () { - if (this.props.searchTerm === '') { - this.props.fetchSuggestions(); - } - } - handleLoadMoreAccounts = () => this.props.expandSearch('accounts'); handleLoadMoreStatuses = () => this.props.expandSearch('statuses'); @@ -48,97 +38,52 @@ class SearchResults extends ImmutablePureComponent { handleLoadMoreHashtags = () => this.props.expandSearch('hashtags'); render () { - const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props; - - if (searchTerm === '' && !suggestions.isEmpty()) { - return ( -
-
-
- - -
- - {suggestions && suggestions.map(suggestion => ( - - ))} -
-
- ); - } + const { results } = this.props; let accounts, statuses, hashtags; - let count = 0; if (results.get('accounts') && results.get('accounts').size > 0) { - count += results.get('accounts').size; accounts = ( -
-
- - {results.get('accounts').map(accountId => )} - - {results.get('accounts').size >= 5 && } -
- ); - } - - if (results.get('statuses') && results.get('statuses').size > 0) { - count += results.get('statuses').size; - statuses = ( -
-
- - {results.get('statuses').map(statusId => )} - - {results.get('statuses').size >= 5 && } -
- ); - } else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) { - statuses = ( -
-
- -
- -
-
+ }> + {withoutLastResult(results.get('accounts')).map(accountId => )} + {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && } + ); } if (results.get('hashtags') && results.get('hashtags').size > 0) { - count += results.get('hashtags').size; hashtags = ( -
-
- - {results.get('hashtags').map(hashtag => )} - - {results.get('hashtags').size >= 5 && } -
+ }> + {withoutLastResult(results.get('hashtags')).map(hashtag => )} + {(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && } + ); } + if (results.get('statuses') && results.get('statuses').size > 0) { + statuses = ( + }> + {withoutLastResult(results.get('statuses')).map(statusId => )} + {(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && } + + ); + } + + return (
- +
{accounts} - {statuses} {hashtags} + {statuses}
); } } -export default injectIntl(SearchResults); +export default SearchResults; diff --git a/app/javascript/mastodon/features/explore/components/search_section.jsx b/app/javascript/mastodon/features/explore/components/search_section.jsx new file mode 100644 index 0000000000..c84e3f7cef --- /dev/null +++ b/app/javascript/mastodon/features/explore/components/search_section.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types'; + +import { FormattedMessage } from 'react-intl'; + +export const SearchSection = ({ title, onClickMore, children }) => ( +
+
+

{title}

+ {onClickMore && } +
+ + {children} +
+); + +SearchSection.propTypes = { + title: PropTypes.node.isRequired, + onClickMore: PropTypes.func, + children: PropTypes.children, +}; \ No newline at end of file diff --git a/app/javascript/mastodon/features/explore/results.jsx b/app/javascript/mastodon/features/explore/results.jsx index 7d1ce69ad0..1b9d2f30db 100644 --- a/app/javascript/mastodon/features/explore/results.jsx +++ b/app/javascript/mastodon/features/explore/results.jsx @@ -9,13 +9,15 @@ import { List as ImmutableList } from 'immutable'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { expandSearch } from 'mastodon/actions/search'; +import { submitSearch, expandSearch } from 'mastodon/actions/search'; import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; -import { LoadMore } from 'mastodon/components/load_more'; -import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { Icon } from 'mastodon/components/icon'; +import ScrollableList from 'mastodon/components/scrollable_list'; import Account from 'mastodon/containers/account_container'; import Status from 'mastodon/containers/status_container'; +import { SearchSection } from './components/search_section'; + const messages = defineMessages({ title: { id: 'search_results.title', defaultMessage: 'Search for {q}' }, }); @@ -24,85 +26,175 @@ const mapStateToProps = state => ({ isLoading: state.getIn(['search', 'isLoading']), results: state.getIn(['search', 'results']), q: state.getIn(['search', 'searchTerm']), + submittedType: state.getIn(['search', 'type']), }); -const appendLoadMore = (id, list, onLoadMore) => { - if (list.size >= 5) { - return list.push(); +const INITIAL_PAGE_LIMIT = 10; +const INITIAL_DISPLAY = 4; + +const hidePeek = list => { + if (list.size > INITIAL_PAGE_LIMIT && list.size % INITIAL_PAGE_LIMIT === 1) { + return list.skipLast(1); } else { return list; } }; -const renderAccounts = (results, onLoadMore) => appendLoadMore('accounts', results.get('accounts', ImmutableList()).map(item => ( - -)), onLoadMore); +const renderAccounts = accounts => hidePeek(accounts).map(id => ( + +)); -const renderHashtags = (results, onLoadMore) => appendLoadMore('hashtags', results.get('hashtags', ImmutableList()).map(item => ( - -)), onLoadMore); +const renderHashtags = hashtags => hidePeek(hashtags).map(hashtag => ( + +)); -const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', results.get('statuses', ImmutableList()).map(item => ( - -)), onLoadMore); +const renderStatuses = statuses => hidePeek(statuses).map(id => ( + +)); class Results extends PureComponent { static propTypes = { - results: ImmutablePropTypes.map, + results: ImmutablePropTypes.contains({ + accounts: ImmutablePropTypes.orderedSet, + statuses: ImmutablePropTypes.orderedSet, + hashtags: ImmutablePropTypes.orderedSet, + }), isLoading: PropTypes.bool, multiColumn: PropTypes.bool, dispatch: PropTypes.func.isRequired, q: PropTypes.string, intl: PropTypes.object, + submittedType: PropTypes.oneOf(['accounts', 'statuses', 'hashtags']), }; state = { - type: 'all', + type: this.props.submittedType || 'all', }; - handleSelectAll = () => this.setState({ type: 'all' }); - handleSelectAccounts = () => this.setState({ type: 'accounts' }); - handleSelectHashtags = () => this.setState({ type: 'hashtags' }); - handleSelectStatuses = () => this.setState({ type: 'statuses' }); - handleLoadMoreAccounts = () => this.loadMore('accounts'); - handleLoadMoreStatuses = () => this.loadMore('statuses'); - handleLoadMoreHashtags = () => this.loadMore('hashtags'); + static getDerivedStateFromProps(props, state) { + if (props.submittedType !== state.type) { + return { + type: props.submittedType || 'all', + }; + } - loadMore (type) { + return null; + }; + + handleSelectAll = () => { + const { submittedType, dispatch } = this.props; + + // If we originally searched for a specific type, we need to resubmit + // the query to get all types of results + if (submittedType) { + dispatch(submitSearch()); + } + + this.setState({ type: 'all' }); + }; + + handleSelectAccounts = () => { + const { submittedType, dispatch } = this.props; + + // If we originally searched for something else (but not everything), + // we need to resubmit the query for this specific type + if (submittedType !== 'accounts') { + dispatch(submitSearch('accounts')); + } + + this.setState({ type: 'accounts' }); + }; + + handleSelectHashtags = () => { + const { submittedType, dispatch } = this.props; + + // If we originally searched for something else (but not everything), + // we need to resubmit the query for this specific type + if (submittedType !== 'hashtags') { + dispatch(submitSearch('hashtags')); + } + + this.setState({ type: 'hashtags' }); + } + + handleSelectStatuses = () => { + const { submittedType, dispatch } = this.props; + + // If we originally searched for something else (but not everything), + // we need to resubmit the query for this specific type + if (submittedType !== 'statuses') { + dispatch(submitSearch('statuses')); + } + + this.setState({ type: 'statuses' }); + } + + handleLoadMoreAccounts = () => this._loadMore('accounts'); + handleLoadMoreStatuses = () => this._loadMore('statuses'); + handleLoadMoreHashtags = () => this._loadMore('hashtags'); + + _loadMore (type) { const { dispatch } = this.props; dispatch(expandSearch(type)); } + handleLoadMore = () => { + const { type } = this.state; + + if (type !== 'all') { + this._loadMore(type); + } + }; + render () { const { intl, isLoading, q, results } = this.props; const { type } = this.state; - let filteredResults = ImmutableList(); + // We request 1 more result than we display so we can tell if there'd be a next page + const hasMore = type !== 'all' ? results.get(type, ImmutableList()).size > INITIAL_PAGE_LIMIT && results.get(type).size % INITIAL_PAGE_LIMIT === 1 : false; + + let filteredResults; if (!isLoading) { + const accounts = results.get('accounts', ImmutableList()); + const hashtags = results.get('hashtags', ImmutableList()); + const statuses = results.get('statuses', ImmutableList()); + switch(type) { case 'all': - filteredResults = filteredResults.concat(renderAccounts(results, this.handleLoadMoreAccounts), renderHashtags(results, this.handleLoadMoreHashtags), renderStatuses(results, this.handleLoadMoreStatuses)); + filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? ( + <> + {accounts.size > 0 && ( + } onClickMore={this.handleLoadMoreAccounts}> + {accounts.take(INITIAL_DISPLAY).map(id => )} + + )} + + {hashtags.size > 0 && ( + } onClickMore={this.handleLoadMoreHashtags}> + {hashtags.take(INITIAL_DISPLAY).map(hashtag => )} + + )} + + {statuses.size > 0 && ( + } onClickMore={this.handleLoadMoreStatuses}> + {statuses.take(INITIAL_DISPLAY).map(id => )} + + )} + + ) : []; break; case 'accounts': - filteredResults = filteredResults.concat(renderAccounts(results, this.handleLoadMoreAccounts)); + filteredResults = renderAccounts(accounts); break; case 'hashtags': - filteredResults = filteredResults.concat(renderHashtags(results, this.handleLoadMoreHashtags)); + filteredResults = renderHashtags(hashtags); break; case 'statuses': - filteredResults = filteredResults.concat(renderStatuses(results, this.handleLoadMoreStatuses)); + filteredResults = renderStatuses(statuses); break; } - - if (filteredResults.size === 0) { - filteredResults = ( -
- -
- ); - } } return ( @@ -115,7 +207,16 @@ class Results extends PureComponent {
- {isLoading ? : filteredResults} + } + bindToDocument + > + {filteredResults} +
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 13cddba723..2a99e8ebfd 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -600,10 +600,9 @@ "search_results.all": "All", "search_results.hashtags": "Hashtags", "search_results.nothing_found": "Could not find anything for these search terms", + "search_results.see_all": "See all", "search_results.statuses": "Posts", - "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.", "search_results.title": "Search for {q}", - "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)", "server_banner.active_users": "active users", "server_banner.administered_by": "Administered by:", @@ -675,8 +674,6 @@ "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.", "subscribed_languages.save": "Save changes", "subscribed_languages.target": "Change subscribed languages for {target}", - "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", "tabs_bar.home": "Home", "tabs_bar.notifications": "Notifications", "time_remaining.days": "{number, plural, one {# day} other {# days}} left", diff --git a/app/javascript/mastodon/reducers/search.js b/app/javascript/mastodon/reducers/search.js index ccef314031..c81d7ff3c8 100644 --- a/app/javascript/mastodon/reducers/search.js +++ b/app/javascript/mastodon/reducers/search.js @@ -1,4 +1,4 @@ -import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; +import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import { COMPOSE_MENTION, @@ -12,6 +12,7 @@ import { SEARCH_FETCH_FAIL, SEARCH_FETCH_SUCCESS, SEARCH_SHOW, + SEARCH_EXPAND_REQUEST, SEARCH_EXPAND_SUCCESS, SEARCH_RESULT_CLICK, SEARCH_RESULT_FORGET, @@ -24,6 +25,7 @@ const initialState = ImmutableMap({ results: ImmutableMap(), isLoading: false, searchTerm: '', + type: null, recent: ImmutableOrderedSet(), }); @@ -37,6 +39,8 @@ export default function search(state = initialState, action) { map.set('results', ImmutableMap()); map.set('submitted', false); map.set('hidden', false); + map.set('searchTerm', ''); + map.set('type', null); }); case SEARCH_SHOW: return state.set('hidden', false); @@ -48,23 +52,27 @@ export default function search(state = initialState, action) { return state.withMutations(map => { map.set('isLoading', true); map.set('submitted', true); + map.set('type', action.searchType); }); case SEARCH_FETCH_FAIL: return state.set('isLoading', false); case SEARCH_FETCH_SUCCESS: return state.withMutations(map => { map.set('results', ImmutableMap({ - accounts: ImmutableList(action.results.accounts.map(item => item.id)), - statuses: ImmutableList(action.results.statuses.map(item => item.id)), - hashtags: fromJS(action.results.hashtags), + accounts: ImmutableOrderedSet(action.results.accounts.map(item => item.id)), + statuses: ImmutableOrderedSet(action.results.statuses.map(item => item.id)), + hashtags: ImmutableOrderedSet(fromJS(action.results.hashtags)), })); map.set('searchTerm', action.searchTerm); + map.set('type', action.searchType); map.set('isLoading', false); }); + case SEARCH_EXPAND_REQUEST: + return state.set('type', action.searchType); case SEARCH_EXPAND_SUCCESS: - const results = action.searchType === 'hashtags' ? fromJS(action.results.hashtags) : action.results[action.searchType].map(item => item.id); - return state.updateIn(['results', action.searchType], list => list.concat(results)); + const results = action.searchType === 'hashtags' ? ImmutableOrderedSet(fromJS(action.results.hashtags)) : action.results[action.searchType].map(item => item.id); + return state.updateIn(['results', action.searchType], list => list.union(results)); case SEARCH_RESULT_CLICK: return state.update('recent', set => set.add(fromJS(action.result))); case SEARCH_RESULT_FORGET: diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 34c1594e82..a9d2dac80f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5172,22 +5172,39 @@ a.status-card { } .search-results__section { - margin-bottom: 5px; + border-bottom: 1px solid lighten($ui-base-color, 8%); - h5 { + &:last-child { + border-bottom: 0; + } + + &__header { background: darken($ui-base-color, 4%); border-bottom: 1px solid lighten($ui-base-color, 8%); - cursor: default; - display: flex; padding: 15px; font-weight: 500; - font-size: 16px; - color: $dark-text-color; + font-size: 14px; + color: $darker-text-color; + display: flex; + justify-content: space-between; - .fa { - display: inline-block; + h3 .fa { margin-inline-end: 5px; } + + button { + color: $highlight-text-color; + padding: 0; + border: 0; + background: 0; + font: inherit; + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } } .account:last-child, @@ -6815,14 +6832,14 @@ a.status-card { .notification__filter-bar, .account__section-headline { - background: darken($ui-base-color, 4%); + background: $ui-base-color; border-bottom: 1px solid lighten($ui-base-color, 8%); cursor: default; display: flex; flex-shrink: 0; button { - background: darken($ui-base-color, 4%); + background: transparent; border: 0; margin: 0; } @@ -6842,26 +6859,18 @@ a.status-card { white-space: nowrap; &.active { - color: $secondary-text-color; + color: $primary-text-color; - &::before, - &::after { + &::before { display: block; content: ''; position: absolute; - bottom: 0; - left: 50%; - width: 0; - height: 0; - transform: translateX(-50%); - border-style: solid; - border-width: 0 10px 10px; - border-color: transparent transparent lighten($ui-base-color, 8%); - } - - &::after { bottom: -1px; - border-color: transparent transparent $ui-base-color; + left: 0; + width: 100%; + height: 3px; + border-radius: 4px; + background: $highlight-text-color; } } } From 2c5ba586ff02b999caa40889c5dd4cc8e164592d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 5 Sep 2023 23:57:03 +0200 Subject: [PATCH 104/182] Remove obfuscation of reply count in web UI (#26768) --- .../mastodon/components/animated_number.tsx | 25 +++---------------- .../mastodon/components/icon_button.tsx | 4 +-- .../mastodon/components/status_action_bar.jsx | 2 +- .../picture_in_picture/components/footer.jsx | 2 +- 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/app/javascript/mastodon/components/animated_number.tsx b/app/javascript/mastodon/components/animated_number.tsx index 05a7e01898..e98e30b242 100644 --- a/app/javascript/mastodon/components/animated_number.tsx +++ b/app/javascript/mastodon/components/animated_number.tsx @@ -6,21 +6,10 @@ import { reduceMotion } from '../initial_state'; import { ShortNumber } from './short_number'; -const obfuscatedCount = (count: number) => { - if (count < 0) { - return 0; - } else if (count <= 1) { - return count; - } else { - return '1+'; - } -}; - interface Props { value: number; - obfuscate?: boolean; } -export const AnimatedNumber: React.FC = ({ value, obfuscate }) => { +export const AnimatedNumber: React.FC = ({ value }) => { const [previousValue, setPreviousValue] = useState(value); const [direction, setDirection] = useState<1 | -1>(1); @@ -36,11 +25,7 @@ export const AnimatedNumber: React.FC = ({ value, obfuscate }) => { ); if (reduceMotion) { - return obfuscate ? ( - <>{obfuscatedCount(value)} - ) : ( - - ); + return ; } const styles = [ @@ -67,11 +52,7 @@ export const AnimatedNumber: React.FC = ({ value, obfuscate }) => { transform: `translateY(${style.y * 100}%)`, }} > - {obfuscate ? ( - obfuscatedCount(data as number) - ) : ( - - )} + ))} diff --git a/app/javascript/mastodon/components/icon_button.tsx b/app/javascript/mastodon/components/icon_button.tsx index 9dbee2cc24..da6f19e9ea 100644 --- a/app/javascript/mastodon/components/icon_button.tsx +++ b/app/javascript/mastodon/components/icon_button.tsx @@ -24,7 +24,6 @@ interface Props { overlay: boolean; tabIndex: number; counter?: number; - obfuscateCount?: boolean; href?: string; ariaHidden: boolean; } @@ -105,7 +104,6 @@ export class IconButton extends PureComponent { tabIndex, title, counter, - obfuscateCount, href, ariaHidden, } = this.props; @@ -131,7 +129,7 @@ export class IconButton extends PureComponent {