Merge remote-tracking branch 'parent/main' into kb_migration

This commit is contained in:
KMY 2023-08-24 09:58:54 +09:00
commit adfa3524fc
38 changed files with 294 additions and 184 deletions

View file

@ -2,3 +2,7 @@ VAGRANT=true
LOCAL_DOMAIN=mastodon.local LOCAL_DOMAIN=mastodon.local
BIND=0.0.0.0 BIND=0.0.0.0
DB_HOST=/var/run/postgresql/ DB_HOST=/var/run/postgresql/
ES_ENABLED=true
ES_HOST=localhost
ES_PORT=9200

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.54.2. # using RuboCop version 1.56.1.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -61,38 +61,8 @@ Lint/EmptyBlock:
- 'spec/fabricators/access_token_fabricator.rb' - 'spec/fabricators/access_token_fabricator.rb'
- 'spec/fabricators/conversation_fabricator.rb' - 'spec/fabricators/conversation_fabricator.rb'
- 'spec/fabricators/system_key_fabricator.rb' - 'spec/fabricators/system_key_fabricator.rb'
- 'spec/helpers/admin/action_logs_helper_spec.rb'
- 'spec/lib/activitypub/adapter_spec.rb' - 'spec/lib/activitypub/adapter_spec.rb'
- 'spec/models/account_alias_spec.rb'
- 'spec/models/account_deletion_request_spec.rb'
- 'spec/models/account_moderation_note_spec.rb'
- 'spec/models/announcement_mute_spec.rb'
- 'spec/models/announcement_reaction_spec.rb'
- 'spec/models/announcement_spec.rb'
- 'spec/models/backup_spec.rb'
- 'spec/models/conversation_mute_spec.rb'
- 'spec/models/custom_filter_keyword_spec.rb'
- 'spec/models/custom_filter_spec.rb'
- 'spec/models/device_spec.rb'
- 'spec/models/encrypted_message_spec.rb'
- 'spec/models/featured_tag_spec.rb'
- 'spec/models/follow_recommendation_suppression_spec.rb'
- 'spec/models/list_account_spec.rb'
- 'spec/models/list_spec.rb'
- 'spec/models/login_activity_spec.rb'
- 'spec/models/mute_spec.rb'
- 'spec/models/preview_card_spec.rb'
- 'spec/models/preview_card_trend_spec.rb'
- 'spec/models/relay_spec.rb'
- 'spec/models/scheduled_status_spec.rb'
- 'spec/models/status_stat_spec.rb'
- 'spec/models/status_trend_spec.rb'
- 'spec/models/system_key_spec.rb'
- 'spec/models/tag_follow_spec.rb'
- 'spec/models/unavailable_domain_spec.rb'
- 'spec/models/user_invite_request_spec.rb'
- 'spec/models/user_role_spec.rb' - 'spec/models/user_role_spec.rb'
- 'spec/models/web/setting_spec.rb'
Lint/NonLocalExitFromIterator: Lint/NonLocalExitFromIterator:
Exclude: Exclude:
@ -135,7 +105,7 @@ Lint/UselessAssignment:
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
Max: 146 Max: 144
# Configuration parameters: CountBlocks, Max. # Configuration parameters: CountBlocks, Max.
Metrics/BlockNesting: Metrics/BlockNesting:
@ -164,6 +134,19 @@ Naming/VariableNumber:
- 'spec/models/domain_block_spec.rb' - 'spec/models/domain_block_spec.rb'
- 'spec/models/user_spec.rb' - 'spec/models/user_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeMultiline.
Performance/DeletePrefix:
Exclude:
- 'app/models/featured_tag.rb'
Performance/MapMethodChain:
Exclude:
- 'app/models/feed.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'spec/services/bulk_import_service_spec.rb'
- 'spec/services/import_service_spec.rb'
RSpec/AnyInstance: RSpec/AnyInstance:
Exclude: Exclude:
- 'spec/controllers/activitypub/inboxes_controller_spec.rb' - 'spec/controllers/activitypub/inboxes_controller_spec.rb'
@ -768,6 +751,15 @@ Style/RedundantFetchBlock:
- 'config/initializers/paperclip.rb' - 'config/initializers/paperclip.rb'
- 'config/puma.rb' - 'config/puma.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Exclude:
- 'app/controllers/api/v1/directories_controller.rb'
- 'app/controllers/auth/confirmations_controller.rb'
- 'app/lib/ostatus/tag_manager.rb'
- 'app/models/form/import.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try! # AllowedMethods: present?, blank?, presence, try, try!

View file

@ -39,47 +39,47 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.0.7) actioncable (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (7.0.7) actionmailbox (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
activejob (= 7.0.7) activejob (= 7.0.7.2)
activerecord (= 7.0.7) activerecord (= 7.0.7.2)
activestorage (= 7.0.7) activestorage (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.0.7) actionmailer (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
actionview (= 7.0.7) actionview (= 7.0.7.2)
activejob (= 7.0.7) activejob (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (7.0.7) actionpack (7.0.7.2)
actionview (= 7.0.7) actionview (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
rack (~> 2.0, >= 2.2.4) rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.7) actiontext (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
activerecord (= 7.0.7) activerecord (= 7.0.7.2)
activestorage (= 7.0.7) activestorage (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.0.7) actionview (7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -89,22 +89,22 @@ GEM
activemodel (>= 4.1, < 7.1) activemodel (>= 4.1, < 7.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.0.7) activejob (7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.0.7) activemodel (7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
activerecord (7.0.7) activerecord (7.0.7.2)
activemodel (= 7.0.7) activemodel (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
activestorage (7.0.7) activestorage (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
activejob (= 7.0.7) activejob (= 7.0.7.2)
activerecord (= 7.0.7) activerecord (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
marcel (~> 1.0) marcel (~> 1.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activesupport (7.0.7) activesupport (7.0.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -147,6 +147,7 @@ GEM
faraday_middleware (~> 1.0, >= 1.0.0.rc1) faraday_middleware (~> 1.0, >= 1.0.0.rc1)
net-http-persistent (~> 4.0) net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8) nokogiri (~> 1, >= 1.10.8)
base64 (0.1.1)
bcrypt (3.1.18) bcrypt (3.1.18)
better_errors (2.10.1) better_errors (2.10.1)
erubi (>= 1.0.0) erubi (>= 1.0.0)
@ -451,7 +452,7 @@ GEM
hashie (~> 5.0) hashie (~> 5.0)
memory_profiler (1.0.1) memory_profiler (1.0.1)
method_source (1.0.0) method_source (1.0.0)
mime-types (3.5.0) mime-types (3.5.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2023.0808) mime-types-data (3.2023.0808)
mini_mime (1.1.5) mini_mime (1.1.5)
@ -481,7 +482,7 @@ GEM
nokogiri (1.15.4) nokogiri (1.15.4)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
oj (3.15.0) oj (3.16.0)
omniauth (2.1.1) omniauth (2.1.1)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 2.2.3) rack (>= 2.2.3)
@ -555,20 +556,20 @@ GEM
rack rack
rack-test (2.1.0) rack-test (2.1.0)
rack (>= 1.3) rack (>= 1.3)
rails (7.0.7) rails (7.0.7.2)
actioncable (= 7.0.7) actioncable (= 7.0.7.2)
actionmailbox (= 7.0.7) actionmailbox (= 7.0.7.2)
actionmailer (= 7.0.7) actionmailer (= 7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
actiontext (= 7.0.7) actiontext (= 7.0.7.2)
actionview (= 7.0.7) actionview (= 7.0.7.2)
activejob (= 7.0.7) activejob (= 7.0.7.2)
activemodel (= 7.0.7) activemodel (= 7.0.7.2)
activerecord (= 7.0.7) activerecord (= 7.0.7.2)
activestorage (= 7.0.7) activestorage (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.0.7) railties (= 7.0.7.2)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -583,9 +584,9 @@ GEM
rails-i18n (7.0.7) rails-i18n (7.0.7)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.0.7) railties (7.0.7.2)
actionpack (= 7.0.7) actionpack (= 7.0.7.2)
activesupport (= 7.0.7) activesupport (= 7.0.7.2)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
@ -639,7 +640,8 @@ GEM
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.12.0) rspec-support (3.12.0)
rspec_chunked (0.6) rspec_chunked (0.6)
rubocop (1.54.2) rubocop (1.56.1)
base64 (~> 0.1.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -647,7 +649,7 @@ GEM
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.0, < 2.0) rubocop-ast (>= 1.28.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0) rubocop-ast (1.29.0)
@ -656,14 +658,14 @@ GEM
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-factory_bot (2.23.1) rubocop-factory_bot (2.23.1)
rubocop (~> 1.33) rubocop (~> 1.33)
rubocop-performance (1.18.0) rubocop-performance (1.19.0)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
rubocop-rails (2.20.2) rubocop-rails (2.20.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.33.0, < 2.0)
rubocop-rspec (2.22.0) rubocop-rspec (2.23.2)
rubocop (~> 1.33) rubocop (~> 1.33)
rubocop-capybara (~> 2.17) rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22) rubocop-factory_bot (~> 2.22)

View file

@ -1,8 +1,11 @@
# Security Policy # Security Policy
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can reach us at <security@joinmastodon.org>. If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
You should _not_ report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk. - open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
- reach us at <security@joinmastodon.org>
You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
## Scope ## Scope

43
Vagrantfile vendored
View file

@ -60,6 +60,37 @@ sudo usermod -a -G rvm $USER
SCRIPT SCRIPT
$provisionElasticsearch = <<SCRIPT
# Install Elastic Search
sudo apt install openjdk-17-jre-headless -y
sudo wget -O /usr/share/keyrings/elasticsearch.asc https://artifacts.elastic.co/GPG-KEY-elasticsearch
sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/elasticsearch.asc] https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elastic-7.x.list'
sudo apt update
sudo apt install elasticsearch -y
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
echo 'path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["localhost"]
cluster.initial_master_nodes: ["node-1"]' > /etc/elasticsearch/elasticsearch.yml
sudo systemctl restart elasticsearch
# Install Kibana
sudo apt install kibana -y
sudo systemctl enable --now kibana
echo 'server.host: "0.0.0.0"
elasticsearch.hosts: ["http://localhost:9200"]' > /etc/kibana/kibana.yml
sudo systemctl restart kibana
SCRIPT
$provisionB = <<SCRIPT $provisionB = <<SCRIPT
source "/etc/profile.d/rvm.sh" source "/etc/profile.d/rvm.sh"
@ -102,10 +133,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider :virtualbox do |vb| config.vm.provider :virtualbox do |vb|
vb.name = "mastodon" vb.name = "mastodon"
vb.customize ["modifyvm", :id, "--memory", "2048"] vb.customize ["modifyvm", :id, "--memory", "8192"]
# Increase the number of CPUs. Uncomment and adjust to vb.customize ["modifyvm", :id, "--cpus", "3"]
# increase performance
# vb.customize ["modifyvm", :id, "--cpus", "3"]
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions. # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
# https://github.com/mitchellh/vagrant/issues/1172 # https://github.com/mitchellh/vagrant/issues/1172
@ -141,9 +170,15 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.network :forwarded_port, guest: 3000, host: 3000 config.vm.network :forwarded_port, guest: 3000, host: 3000
config.vm.network :forwarded_port, guest: 4000, host: 4000 config.vm.network :forwarded_port, guest: 4000, host: 4000
config.vm.network :forwarded_port, guest: 8080, host: 8080 config.vm.network :forwarded_port, guest: 8080, host: 8080
config.vm.network :forwarded_port, guest: 9200, host: 9200
config.vm.network :forwarded_port, guest: 9300, host: 9300
config.vm.network :forwarded_port, guest: 9243, host: 9243
config.vm.network :forwarded_port, guest: 5601, host: 5601
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision' # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true
# Run with elevated privileges for Elasticsearch installation
config.vm.provision :shell, inline: $provisionElasticsearch, privileged: true
config.vm.provision :shell, inline: $provisionB, privileged: false config.vm.provision :shell, inline: $provisionB, privileged: false
config.vm.post_up_message = <<MESSAGE config.vm.post_up_message = <<MESSAGE

View file

@ -21,6 +21,7 @@ module ContextHelper
blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' }, indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' },
memorial: { 'toot' => 'http://joinmastodon.org/ns#', 'memorial' => 'toot:memorial' },
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
emoji_reactions: { 'fedibird' => 'http://fedibird.com/ns#', 'emojiReactions' => { '@id' => 'fedibird:emojiReactions', '@type' => '@id' } }, emoji_reactions: { 'fedibird' => 'http://fedibird.com/ns#', 'emojiReactions' => { '@id' => 'fedibird:emojiReactions', '@type' => '@id' } },
searchable_by: { 'fedibird' => 'http://fedibird.com/ns#', 'searchableBy' => { '@id' => 'fedibird:searchableBy', '@type' => '@id' } }, searchable_by: { 'fedibird' => 'http://fedibird.com/ns#', 'searchableBy' => { '@id' => 'fedibird:searchableBy', '@type' => '@id' } },

View file

@ -188,6 +188,7 @@ module LanguagesHelper
ISO_639_3 = { ISO_639_3 = {
ast: ['Asturian', 'Asturianu'].freeze, ast: ['Asturian', 'Asturianu'].freeze,
chr: ['Cherokee', 'ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ'].freeze,
ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze, ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze,
cnr: ['Montenegrin', 'crnogorski'].freeze, cnr: ['Montenegrin', 'crnogorski'].freeze,
jbo: ['Lojban', 'la .lojban.'].freeze, jbo: ['Lojban', 'la .lojban.'].freeze,
@ -200,6 +201,7 @@ module LanguagesHelper
smj: ['Lule Sami', 'Julevsámegiella'].freeze, smj: ['Lule Sami', 'Julevsámegiella'].freeze,
szl: ['Silesian', 'ślůnsko godka'].freeze, szl: ['Silesian', 'ślůnsko godka'].freeze,
tok: ['Toki Pona', 'toki pona'].freeze, tok: ['Toki Pona', 'toki pona'].freeze,
xal: ['Kalmyk', 'Хальмг келн'].freeze,
zba: ['Balaibalan', 'باليبلن'].freeze, zba: ['Balaibalan', 'باليبلن'].freeze,
zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze, zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze,
}.freeze }.freeze

View file

@ -105,6 +105,21 @@ describe('computeHashtagBarForStatus', () => {
); );
}); });
it('handles server-side normalized tags with accentuated characters', () => {
const status = createStatus(
'<p>Text</p><p><a href="test">#éaa</a> <a href="test">#Éaa</a></p>',
['eaa'], // The server may normalize the hashtags in the `tags` attribute
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual(['Éaa']);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Text</p>"`,
);
});
it('does not display in bar a hashtag in content with a case difference', () => { it('does not display in bar a hashtag in content with a case difference', () => {
const status = createStatus( const status = createStatus(
'<p>Text <a href="test">#Éaa</a></p><p><a href="test">#éaa</a></p>', '<p>Text <a href="test">#Éaa</a></p><p><a href="test">#éaa</a></p>',

View file

@ -23,8 +23,9 @@ export type StatusLike = Record<{
}>; }>;
function normalizeHashtag(hashtag: string) { function normalizeHashtag(hashtag: string) {
if (hashtag && hashtag.startsWith('#')) return hashtag.slice(1); return (
else return hashtag; hashtag && hashtag.startsWith('#') ? hashtag.slice(1) : hashtag
).normalize('NFKC');
} }
function isNodeLinkHashtag(element: Node): element is HTMLLinkElement { function isNodeLinkHashtag(element: Node): element is HTMLLinkElement {
@ -70,9 +71,16 @@ function uniqueHashtagsWithCaseHandling(hashtags: string[]) {
} }
// Create the collator once, this is much more efficient // Create the collator once, this is much more efficient
const collator = new Intl.Collator(undefined, { sensitivity: 'accent' }); const collator = new Intl.Collator(undefined, {
sensitivity: 'base', // we use this to emulate the ASCII folding done on the server-side, hopefuly more efficiently
});
function localeAwareInclude(collection: string[], value: string) { function localeAwareInclude(collection: string[], value: string) {
return collection.find((item) => collator.compare(item, value) === 0); const normalizedValue = value.normalize('NFKC');
return !!collection.find(
(item) => collator.compare(item.normalize('NFKC'), normalizedValue) === 0,
);
} }
// We use an intermediate function here to make it easier to test // We use an intermediate function here to make it easier to test
@ -121,11 +129,13 @@ export function computeHashtagBarForStatus(status: StatusLike): {
// try to see if the last line is only hashtags // try to see if the last line is only hashtags
let onlyHashtags = true; let onlyHashtags = true;
const normalizedTagNames = tagNames.map((tag) => tag.normalize('NFKC'));
Array.from(lastChild.childNodes).forEach((node) => { Array.from(lastChild.childNodes).forEach((node) => {
if (isNodeLinkHashtag(node) && node.textContent) { if (isNodeLinkHashtag(node) && node.textContent) {
const normalized = normalizeHashtag(node.textContent); const normalized = normalizeHashtag(node.textContent);
if (!localeAwareInclude(tagNames, normalized)) { if (!localeAwareInclude(normalizedTagNames, normalized)) {
// stop here, this is not a real hashtag, so consider it as text // stop here, this is not a real hashtag, so consider it as text
onlyHashtags = false; onlyHashtags = false;
return; return;
@ -140,12 +150,14 @@ export function computeHashtagBarForStatus(status: StatusLike): {
} }
}); });
const hashtagsInBar = tagNames.filter( const hashtagsInBar = tagNames.filter((tag) => {
(tag) => const normalizedTag = tag.normalize('NFKC');
// the tag does not appear at all in the status content, it is an out-of-band tag // the tag does not appear at all in the status content, it is an out-of-band tag
!localeAwareInclude(contentHashtags, tag) && return (
!localeAwareInclude(lastLineHashtags, tag), !localeAwareInclude(contentHashtags, normalizedTag) &&
!localeAwareInclude(lastLineHashtags, normalizedTag)
); );
});
const isOnlyOneLine = contentWithoutLastLine.content.childElementCount === 0; const isOnlyOneLine = contentWithoutLastLine.content.childElementCount === 0;
const hasMedia = status.get('media_attachments').size > 0; const hasMedia = status.get('media_attachments').size > 0;
@ -204,7 +216,7 @@ const HashtagBar: React.FC<{
<div className='hashtag-bar'> <div className='hashtag-bar'>
{revealedHashtags.map((hashtag) => ( {revealedHashtags.map((hashtag) => (
<Link key={hashtag} to={`/tags/${hashtag}`}> <Link key={hashtag} to={`/tags/${hashtag}`}>
#{hashtag} #<span>{hashtag}</span>
</Link> </Link>
))} ))}

View file

@ -583,6 +583,7 @@ class Status extends ImmutablePureComponent {
} }
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
const expanded = !status.get('hidden')
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>
@ -611,7 +612,7 @@ class Status extends ImmutablePureComponent {
<StatusContent <StatusContent
status={status} status={status}
onClick={this.handleClick} onClick={this.handleClick}
expanded={!status.get('hidden')} expanded={expanded}
onExpandedToggle={this.handleExpandedToggle} onExpandedToggle={this.handleExpandedToggle}
onTranslate={this.handleTranslate} onTranslate={this.handleTranslate}
collapsible collapsible
@ -621,7 +622,7 @@ class Status extends ImmutablePureComponent {
{(!isCardMediaWithSensitive || !status.get('hidden')) && media} {(!isCardMediaWithSensitive || !status.get('hidden')) && media}
{hashtagBar} {expanded && hashtagBar}
{emojiReactionsBar} {emojiReactionsBar}

View file

@ -108,10 +108,10 @@ class Results extends PureComponent {
return ( return (
<> <>
<div className='account__section-headline'> <div className='account__section-headline'>
<button onClick={this.handleSelectAll} className={type === 'all' && 'active'}><FormattedMessage id='search_results.all' defaultMessage='All' /></button> <button onClick={this.handleSelectAll} className={type === 'all' ? 'active' : undefined}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
<button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button> <button onClick={this.handleSelectAccounts} className={type === 'accounts' ? 'active' : undefined}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button>
<button onClick={this.handleSelectHashtags} className={type === 'hashtags' && 'active'}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button> <button onClick={this.handleSelectHashtags} className={type === 'hashtags' ? 'active' : undefined}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
<button onClick={this.handleSelectStatuses} className={type === 'statuses' && 'active'}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button> <button onClick={this.handleSelectStatuses} className={type === 'statuses' ? 'active' : undefined}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
</div> </div>
<div className='explore__search-results'> <div className='explore__search-results'>

View file

@ -372,6 +372,7 @@ class DetailedStatus extends ImmutablePureComponent {
} }
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
const expanded = !status.get('hidden')
return ( return (
<div style={outerStyle}> <div style={outerStyle}>
@ -397,7 +398,7 @@ class DetailedStatus extends ImmutablePureComponent {
{(!isCardMediaWithSensitive || !status.get('hidden')) && media} {(!isCardMediaWithSensitive || !status.get('hidden')) && media}
{hashtagBar} {expanded && hashtagBar}
{emojiReactionsBar} {emojiReactionsBar}

View file

@ -609,7 +609,7 @@ class Status extends ImmutablePureComponent {
onMoveUp={this.handleMoveUp} onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown} onMoveDown={this.handleMoveDown}
contextType='thread' contextType='thread'
previousId={i > 0 && list.get(i - 1)} previousId={i > 0 ? list.get(i - 1) : undefined}
nextId={list.get(i + 1) || (ancestors && statusId)} nextId={list.get(i + 1) || (ancestors && statusId)}
rootId={statusId} rootId={statusId}
/> />

View file

@ -123,7 +123,10 @@ export default class ModalRoot extends PureComponent {
{visible && ( {visible && (
<> <>
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
{(SpecificComponent) => <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={this.setModalRef} />} {(SpecificComponent) => {
const ref = typeof SpecificComponent !== 'function' ? this.setModalRef : undefined;
return <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={ref} />
}}
</BundleContainer> </BundleContainer>
<Helmet> <Helmet>

View file

@ -5338,6 +5338,7 @@ a.status-card {
&.active { &.active {
transform: rotate(90deg); transform: rotate(90deg);
opacity: 1;
} }
&:hover { &:hover {
@ -8898,6 +8899,44 @@ noscript {
} }
} }
&__choices {
display: flex;
gap: 40px;
&__choice {
flex: 1;
box-sizing: border-box;
h3 {
margin-bottom: 20px;
}
p {
color: $darker-text-color;
margin-bottom: 20px;
font-size: 15px;
}
.button {
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
}
}
@media screen and (max-width: $no-gap-breakpoint - 1px) {
&__choices {
flex-direction: column;
&__choice {
margin-top: 40px;
}
}
}
.link-button { .link-button {
font-size: inherit; font-size: inherit;
display: inline; display: inline;
@ -9620,16 +9659,15 @@ noscript {
a { a {
display: inline-flex; display: inline-flex;
border-radius: 4px; color: $dark-text-color;
background: rgba($highlight-text-color, 0.2);
color: $highlight-text-color;
padding: 0.4em 0.6em;
text-decoration: none; text-decoration: none;
&:hover, &:hover {
&:focus, text-decoration: none;
&:active {
background: rgba($highlight-text-color, 0.3); span {
text-decoration: underline;
}
} }
} }
} }

View file

@ -41,7 +41,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
Admin::SystemCheck::Message.new(:elasticsearch_health_red) Admin::SystemCheck::Message.new(:elasticsearch_health_red)
elsif cluster_health['number_of_nodes'] < 2 && es_preset != 'single_node_cluster' elsif cluster_health['number_of_nodes'] < 2 && es_preset != 'single_node_cluster'
Admin::SystemCheck::Message.new(:elasticsearch_preset_single_node, nil, 'https://docs.joinmastodon.org/admin/optional/elasticsearch/#scaling') Admin::SystemCheck::Message.new(:elasticsearch_preset_single_node, nil, 'https://docs.joinmastodon.org/admin/optional/elasticsearch/#scaling')
elsif Chewy.client.indices.get_settings['chewy_specifications'].dig('settings', 'index', 'number_of_replicas')&.to_i&.positive? && es_preset == 'single_node_cluster' elsif Chewy.client.indices.get_settings[Chewy::Stash::Specification.index_name]&.dig('settings', 'index', 'number_of_replicas')&.to_i&.positive? && es_preset == 'single_node_cluster'
Admin::SystemCheck::Message.new(:elasticsearch_reset_chewy) Admin::SystemCheck::Message.new(:elasticsearch_reset_chewy)
elsif cluster_health['status'] == 'yellow' elsif cluster_health['status'] == 'yellow'
Admin::SystemCheck::Message.new(:elasticsearch_health_yellow) Admin::SystemCheck::Message.new(:elasticsearch_health_yellow)

View file

@ -68,8 +68,8 @@ class Importer::BaseImporter
protected protected
def in_work_unit(*args, &block) def in_work_unit(...)
work_unit = Concurrent::Promises.future_on(@executor, *args, &block) work_unit = Concurrent::Promises.future_on(@executor, ...)
work_unit.on_fulfillment!(&@on_progress) work_unit.on_fulfillment!(&@on_progress)
work_unit.on_rejection!(&@on_failure) work_unit.on_rejection!(&@on_failure)

View file

@ -46,11 +46,11 @@ class PublicFeed
end end
def local_only? def local_only?
options[:local] options[:local] && !options[:remote]
end end
def remote_only? def remote_only?
options[:remote] options[:remote] && !options[:local]
end end
def hide_local_users? def hide_local_users?

View file

@ -8,13 +8,13 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
context_extensions :manually_approves_followers, :featured, :also_known_as, context_extensions :manually_approves_followers, :featured, :also_known_as,
:moved_to, :property_value, :discoverable, :olm, :suspended, :searchable_by, :subscribable_by, :moved_to, :property_value, :discoverable, :olm, :suspended, :searchable_by, :subscribable_by,
:other_setting :other_setting, :memorial
attributes :id, :type, :following, :followers, attributes :id, :type, :following, :followers,
:inbox, :outbox, :featured, :featured_tags, :inbox, :outbox, :featured, :featured_tags,
:preferred_username, :name, :summary, :preferred_username, :name, :summary,
:url, :manually_approves_followers, :url, :manually_approves_followers,
:discoverable, :published, :searchable_by, :subscribable_by, :other_setting :discoverable, :published, :searchable_by, :subscribable_by, :other_setting, :memorial
has_one :public_key, serializer: ActivityPub::PublicKeySerializer has_one :public_key, serializer: ActivityPub::PublicKeySerializer

View file

@ -147,7 +147,7 @@ class AccountSearchService < BaseService
multi_match: { multi_match: {
query: @query, query: @query,
type: 'bool_prefix', type: 'bool_prefix',
fields: %w(username username.* display_name display_name.*), fields: %w(username^2 username.*^2 display_name display_name.*),
}, },
} }
end end

View file

@ -127,6 +127,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.searchability = searchability_from_audience @account.searchability = searchability_from_audience
@account.dissubscribable = !subscribable(@account.note) @account.dissubscribable = !subscribable(@account.note)
@account.settings = other_settings @account.settings = other_settings
@account.memorial = @json['memorial'] || false
end end
def valid_account? def valid_account?

View file

@ -2,6 +2,7 @@
const { basename, dirname, join, relative, resolve } = require('path'); const { basename, dirname, join, relative, resolve } = require('path');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const { sync } = require('glob'); const { sync } = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const extname = require('path-complete-extname'); const extname = require('path-complete-extname');
@ -84,6 +85,9 @@ module.exports = {
writeToDisk: true, writeToDisk: true,
publicPath: true, publicPath: true,
}), }),
new CircularDependencyPlugin({
failOnError: true,
})
], ],
resolve: { resolve: {

View file

@ -37,14 +37,16 @@ module Paperclip
@output_options['f'] = 'image2' @output_options['f'] = 'image2'
@output_options['vframes'] = 1 @output_options['vframes'] = 1
when 'mp4' when 'mp4'
unless eligible_to_passthrough?(metadata)
@output_options['acodec'] = 'aac' @output_options['acodec'] = 'aac'
@output_options['strict'] = 'experimental' @output_options['strict'] = 'experimental'
if high_vfr?(metadata) && !eligible_to_passthrough?(metadata) if high_vfr?(metadata)
@output_options['vsync'] = 'vfr' @output_options['vsync'] = 'vfr'
@output_options['r'] = @vfr_threshold @output_options['r'] = @vfr_threshold
end end
end end
end
command_arguments, interpolations = prepare_command(destination) command_arguments, interpolations = prepare_command(destination)

View file

@ -2,8 +2,8 @@
class Redis class Redis
module NamespaceExtensions module NamespaceExtensions
def exists?(*args, &block) def exists?(...)
call_with_namespace('exists?', *args, &block) call_with_namespace('exists?', ...)
end end
end end
end end

View file

@ -59,6 +59,7 @@
"babel-plugin-preval": "^5.1.0", "babel-plugin-preval": "^5.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"blurhash": "^2.0.5", "blurhash": "^2.0.5",
"circular-dependency-plugin": "^5.2.2",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"cocoon-js-vanilla": "^1.3.0", "cocoon-js-vanilla": "^1.3.0",
"color-blend": "^4.0.0", "color-blend": "^4.0.0",

View file

@ -16,8 +16,7 @@ describe Api::V1::Instances::TranslationLanguagesController do
context 'when a translation service is configured' do context 'when a translation service is configured' do
before do before do
service = instance_double(TranslationService::DeepL, languages: { nil => %w(en de), 'en' => ['de'] }) service = instance_double(TranslationService::DeepL, languages: { nil => %w(en de), 'en' => ['de'] })
allow(TranslationService).to receive(:configured?).and_return(true) allow(TranslationService).to receive_messages(configured?: true, configured: service)
allow(TranslationService).to receive(:configured).and_return(service)
end end
it 'returns language matrix' do it 'returns language matrix' do

View file

@ -20,8 +20,7 @@ describe Api::V1::Statuses::TranslationsController do
before do before do
translation = TranslationService::Translation.new(text: 'Hello') translation = TranslationService::Translation.new(text: 'Hello')
service = instance_double(TranslationService::DeepL, translate: [translation]) service = instance_double(TranslationService::DeepL, translate: [translation])
allow(TranslationService).to receive(:configured?).and_return(true) allow(TranslationService).to receive_messages(configured?: true, configured: service)
allow(TranslationService).to receive(:configured).and_return(service)
Rails.cache.write('translation_service/languages', { 'es' => ['en'] }) Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
post :create, params: { status_id: status.id } post :create, params: { status_id: status.id }
end end

View file

@ -7,8 +7,7 @@ describe Admin::FilterHelper do
params = ActionController::Parameters.new( params = ActionController::Parameters.new(
{ test: 'test' } { test: 'test' }
) )
allow(helper).to receive(:params).and_return(params) allow(helper).to receive_messages(params: params, url_for: '/test')
allow(helper).to receive(:url_for).and_return('/test')
result = helper.filter_link_to('text', { resolved: true }) result = helper.filter_link_to('text', { resolved: true })
expect(result).to match(/text/) expect(result).to match(/text/)

View file

@ -31,9 +31,7 @@ describe ApplicationHelper do
context 'with a body class string from a controller' do context 'with a body class string from a controller' do
before do before do
without_partial_double_verification do without_partial_double_verification do
allow(helper).to receive(:body_class_string).and_return('modal-layout compose-standalone') allow(helper).to receive_messages(body_class_string: 'modal-layout compose-standalone', current_theme: 'default', current_account: Fabricate(:account))
allow(helper).to receive(:current_theme).and_return('default')
allow(helper).to receive(:current_account).and_return(Fabricate(:account))
end end
end end

View file

@ -25,8 +25,7 @@ RSpec.describe HomeHelper do
it 'returns a link to the account' do it 'returns a link to the account' do
without_partial_double_verification do without_partial_double_verification do
allow(helper).to receive(:current_account).and_return(account) allow(helper).to receive_messages(current_account: account, prefers_autoplay?: false)
allow(helper).to receive(:prefers_autoplay?).and_return(false)
result = helper.account_link_to(account) result = helper.account_link_to(account)
expect(result).to match "@#{account.acct}" expect(result).to match "@#{account.acct}"
@ -101,8 +100,7 @@ RSpec.describe HomeHelper do
context 'with open registrations' do context 'with open registrations' do
it 'returns correct sign up message' do it 'returns correct sign up message' do
allow(helper).to receive(:closed_registrations?).and_return(false) allow(helper).to receive_messages(closed_registrations?: false, open_registrations?: true)
allow(helper).to receive(:open_registrations?).and_return(true)
result = helper.sign_up_message result = helper.sign_up_message
expect(result).to eq t('auth.register') expect(result).to eq t('auth.register')
@ -111,9 +109,7 @@ RSpec.describe HomeHelper do
context 'with approved registrations' do context 'with approved registrations' do
it 'returns correct sign up message' do it 'returns correct sign up message' do
allow(helper).to receive(:closed_registrations?).and_return(false) allow(helper).to receive_messages(closed_registrations?: false, open_registrations?: false, approved_registrations?: true)
allow(helper).to receive(:open_registrations?).and_return(false)
allow(helper).to receive(:approved_registrations?).and_return(true)
result = helper.sign_up_message result = helper.sign_up_message
expect(result).to eq t('auth.apply_for_account') expect(result).to eq t('auth.apply_for_account')

View file

@ -14,13 +14,12 @@ describe Admin::SystemCheck::ElasticsearchCheck do
before do before do
allow(Chewy).to receive(:enabled?).and_return(true) allow(Chewy).to receive(:enabled?).and_return(true)
allow(Chewy.client.cluster).to receive(:health).and_return({ 'status' => 'green', 'number_of_nodes' => 1 }) allow(Chewy.client.cluster).to receive(:health).and_return({ 'status' => 'green', 'number_of_nodes' => 1 })
allow(Chewy.client.indices).to receive(:get_mapping).and_return({ allow(Chewy.client.indices).to receive_messages(get_mapping: {
AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys, AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys,
StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys, StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys,
InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys, InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys,
TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys, TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys,
}) }, get_settings: {
allow(Chewy.client.indices).to receive(:get_settings).and_return({
'chewy_specifications' => { 'chewy_specifications' => {
'settings' => { 'settings' => {
'index' => { 'index' => {

View file

@ -23,8 +23,7 @@ describe ReportFilter do
it 'combines filters on Report' do it 'combines filters on Report' do
filter = described_class.new(account_id: '123', resolved: true, target_account_id: '456') filter = described_class.new(account_id: '123', resolved: true, target_account_id: '456')
allow(Report).to receive(:where).and_return(Report.none) allow(Report).to receive_messages(where: Report.none, resolved: Report.none)
allow(Report).to receive(:resolved).and_return(Report.none)
filter.results filter.results
expect(Report).to have_received(:where).with(account_id: '123') expect(Report).to have_received(:where).with(account_id: '123')
expect(Report).to have_received(:where).with(target_account_id: '456') expect(Report).to have_received(:where).with(target_account_id: '456')

View file

@ -56,6 +56,13 @@ describe 'Public' do
it_behaves_like 'a successful request to the public timeline' it_behaves_like 'a successful request to the public timeline'
end end
context 'with local and remote params' do
let(:params) { { local: true, remote: true } }
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'a successful request to the public timeline'
end
context 'with only_media param' do context 'with only_media param' do
let(:params) { { only_media: true } } let(:params) { { only_media: true } }
let(:expected_statuses) { [media_status] } let(:expected_statuses) { [media_status] }

View file

@ -10,8 +10,7 @@ RSpec.describe SuspendAccountService, type: :service do
let!(:list) { Fabricate(:list, account: local_follower) } let!(:list) { Fabricate(:list, account: local_follower) }
before do before do
allow(FeedManager.instance).to receive(:unmerge_from_home).and_return(nil) allow(FeedManager.instance).to receive_messages(unmerge_from_home: nil, unmerge_from_list: nil)
allow(FeedManager.instance).to receive(:unmerge_from_list).and_return(nil)
local_follower.follow!(account) local_follower.follow!(account)
list.accounts << account list.accounts << account

View file

@ -29,8 +29,7 @@ RSpec.describe TranslateStatusService, type: :service do
end end
end end
allow(TranslationService).to receive(:configured?).and_return(true) allow(TranslationService).to receive_messages(configured?: true, configured: translation_service)
allow(TranslationService).to receive(:configured).and_return(translation_service)
end end
it 'returns translated status content' do it 'returns translated status content' do

View file

@ -10,8 +10,7 @@ RSpec.describe UnsuspendAccountService, type: :service do
let!(:list) { Fabricate(:list, account: local_follower) } let!(:list) { Fabricate(:list, account: local_follower) }
before do before do
allow(FeedManager.instance).to receive(:merge_into_home).and_return(nil) allow(FeedManager.instance).to receive_messages(merge_into_home: nil, merge_into_list: nil)
allow(FeedManager.instance).to receive(:merge_into_list).and_return(nil)
local_follower.follow!(account) local_follower.follow!(account)
list.accounts << account list.accounts << account

View file

@ -4,15 +4,9 @@ require 'rails_helper'
describe 'statuses/show.html.haml', without_verify_partial_doubles: true do describe 'statuses/show.html.haml', without_verify_partial_doubles: true do
before do before do
allow(view).to receive(:api_oembed_url).and_return('') allow(view).to receive_messages(api_oembed_url: '', show_landing_strip?: true, site_title: 'example site', site_hostname: 'example.com', full_asset_url: '//asset.host/image.svg', current_account: nil, single_user_mode?: false)
allow(view).to receive(:show_landing_strip?).and_return(true)
allow(view).to receive(:site_title).and_return('example site')
allow(view).to receive(:site_hostname).and_return('example.com')
allow(view).to receive(:full_asset_url).and_return('//asset.host/image.svg')
allow(view).to receive(:local_time) allow(view).to receive(:local_time)
allow(view).to receive(:local_time_ago) allow(view).to receive(:local_time_ago)
allow(view).to receive(:current_account).and_return(nil)
allow(view).to receive(:single_user_mode?).and_return(false)
assign(:instance_presenter, InstancePresenter.new) assign(:instance_presenter, InstancePresenter.new)
end end

View file

@ -4008,6 +4008,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1" inherits "^2.0.1"
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
circular-dependency-plugin@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600"
integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==
cjs-module-lexer@^1.0.0: cjs-module-lexer@^1.0.0:
version "1.2.3" version "1.2.3"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
@ -6655,9 +6660,9 @@ immutable@^3.8.2:
integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg== integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==
immutable@^4.0.0, immutable@^4.0.0-rc.1, immutable@^4.3.0: immutable@^4.0.0, immutable@^4.0.0-rc.1, immutable@^4.3.0:
version "4.3.2" version "4.3.3"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.2.tgz#f89d910f8dfb6e15c03b2cae2faaf8c1f66455fe" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.3.tgz#8934ff6826d996a7642c8dc4b46e694dd19561e3"
integrity sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA== integrity sha512-808ZFYMsIRAjLAu5xkKo0TsbY9LBy9H5MazTKIEHerNkg0ymgilGfBPMR/3G7d/ihGmuK2Hw8S1izY2d3kd3wA==
import-fresh@^3.2.1: import-fresh@^3.2.1:
version "3.3.0" version "3.3.0"
@ -10133,9 +10138,9 @@ react-test-renderer@^18.2.0:
scheduler "^0.23.0" scheduler "^0.23.0"
react-textarea-autosize@*, react-textarea-autosize@^8.4.1: react-textarea-autosize@*, react-textarea-autosize@^8.4.1:
version "8.5.2" version "8.5.3"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz#6421df2b5b50b9ca8c5e96fd31be688ea7fa2f9d" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409"
integrity sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg== integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==
dependencies: dependencies:
"@babel/runtime" "^7.20.13" "@babel/runtime" "^7.20.13"
use-composed-ref "^1.3.0" use-composed-ref "^1.3.0"
@ -10646,9 +10651,9 @@ sass-loader@^10.2.0:
semver "^7.3.2" semver "^7.3.2"
sass@^1.62.1: sass@^1.62.1:
version "1.65.1" version "1.66.1"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.65.1.tgz#8f283b0c26335a88246a448d22e1342ba2ea1432" resolved "https://registry.yarnpkg.com/sass/-/sass-1.66.1.tgz#04b51c4671e4650aa393740e66a4e58b44d055b1"
integrity sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA== integrity sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0" immutable "^4.0.0"
@ -11333,6 +11338,7 @@ stringz@^2.1.0:
char-regex "^1.0.2" char-regex "^1.0.2"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: "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" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==