diff --git a/.browserslistrc b/.browserslistrc
index 54dd3aaf3..0376af4bc 100644
--- a/.browserslistrc
+++ b/.browserslistrc
@@ -1,7 +1,9 @@
[production]
defaults
-not IE 11
+> 0.2%
+ios >= 15.6
not dead
+not OperaMini all
[development]
supports es6-module
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index dab99829a..e92608a43 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -125,6 +125,22 @@
],
groupName: null, // We dont want them to belong to any group
},
+ {
+ // Group all RuboCop packages with `rubocop` in the same PR
+ matchManagers: ['bundler'],
+ matchPackageNames: ['rubocop'],
+ matchPackagePrefixes: ['rubocop-'],
+ matchUpdateTypes: ['patch', 'minor'],
+ groupName: 'RuboCop (non-major)',
+ },
+ {
+ // Group all RSpec packages with `rspec` in the same PR
+ matchManagers: ['bundler'],
+ matchPackageNames: ['rspec'],
+ matchPackagePrefixes: ['rspec-'],
+ matchUpdateTypes: ['patch', 'minor'],
+ groupName: 'RSpec (non-major)',
+ },
// Add labels depending on package manager
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 7fd259ae0..624c3b7a2 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -114,6 +114,7 @@ jobs:
- '3.0'
- '3.1'
- '.ruby-version'
+ - '3.3'
steps:
- uses: actions/checkout@v4
@@ -189,6 +190,7 @@ jobs:
- '3.0'
- '3.1'
- '.ruby-version'
+ - '3.3'
steps:
- uses: actions/checkout@v4
@@ -288,6 +290,7 @@ jobs:
- '3.0'
- '3.1'
- '.ruby-version'
+ - '3.3'
search-image:
- docker.elastic.co/elasticsearch/elasticsearch:7.17.13
include:
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 8cfcaec8d..2b553ca56 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -1,5 +1,3 @@
-inherits_from: .haml-lint_todo.yml
-
exclude:
- 'vendor/**/*'
- lib/templates/haml/scaffold/_form.html.haml
@@ -14,3 +12,5 @@ linters:
enabled: true
LineLength:
max: 320
+ ViewLength:
+ max: 200 # Override default value of 100 inherited from rubocop
diff --git a/Gemfile b/Gemfile
index 3a1ff81d4..35b9ee91d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -58,6 +58,7 @@ gem 'htmlentities', '~> 4.3'
gem 'http', '~> 5.1'
gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.6.2'
+gem 'i18n', '1.14.1' # TODO: Remove version when resolved: https://github.com/glebm/i18n-tasks/issues/552 / https://github.com/ruby-i18n/i18n/pull/688
gem 'idn-ruby', require: 'idn'
gem 'inline_svg'
gem 'kaminari', '~> 1.2'
@@ -89,7 +90,7 @@ gem 'sidekiq-bulk', '~> 0.2.0'
gem 'simple-navigation', '~> 4.4'
gem 'simple_form', '~> 5.2'
gem 'stoplight', '~> 3.0.1'
-gem 'strong_migrations', '1.7.0'
+gem 'strong_migrations', '1.8.0'
gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9c5bb940b..b341ca84b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -139,7 +139,7 @@ GEM
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
- bigdecimal (3.1.6)
+ bigdecimal (3.1.7)
bindata (2.4.15)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
@@ -213,7 +213,7 @@ GEM
devise_pam_authenticatable2 (9.2.0)
devise (>= 4.0.0)
rpam2 (~> 4.0)
- diff-lcs (1.5.0)
+ diff-lcs (1.5.1)
discard (1.3.0)
activerecord (>= 4.2, < 8)
docile (1.4.0)
@@ -225,8 +225,7 @@ GEM
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
- drb (2.2.0)
- ruby2_keywords
+ drb (2.2.1)
ed25519 (1.3.0)
elasticsearch (7.13.3)
elasticsearch-api (= 7.13.3)
@@ -438,7 +437,7 @@ GEM
mime-types-data (3.2023.1205)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
- minitest (5.21.2)
+ minitest (5.22.3)
msgpack (1.7.2)
multi_json (1.15.0)
multipart-post (2.3.0)
@@ -500,7 +499,7 @@ GEM
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
orm_adapter (0.5.0)
- ox (2.14.17)
+ ox (2.14.18)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
@@ -535,7 +534,7 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.7.3)
- rack (2.2.8.1)
+ rack (2.2.9)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-cors (2.0.2)
@@ -583,7 +582,7 @@ GEM
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
- rails-i18n (7.0.8)
+ rails-i18n (7.0.9)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
railties (7.1.3.2)
@@ -601,7 +600,7 @@ GEM
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.7.0)
rdf (~> 3.3)
- rdoc (6.6.2)
+ rdoc (6.6.3.1)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
@@ -635,21 +634,21 @@ GEM
rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
- rspec-rails (6.1.1)
+ rspec-rails (6.1.2)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
- rspec-core (~> 3.12)
- rspec-expectations (~> 3.12)
- rspec-mocks (~> 3.12)
- rspec-support (~> 3.12)
+ rspec-core (~> 3.13)
+ rspec-expectations (~> 3.13)
+ rspec-mocks (~> 3.13)
+ rspec-support (~> 3.13)
rspec-sidekiq (4.1.0)
rspec-core (~> 3.0)
rspec-expectations (~> 3.0)
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8)
rspec-support (3.13.1)
- rubocop (1.60.2)
+ rubocop (1.62.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@@ -657,24 +656,24 @@ GEM
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
- rubocop-ast (>= 1.30.0, < 2.0)
+ rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.30.0)
- parser (>= 3.2.1.0)
+ rubocop-ast (1.31.2)
+ parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
- rubocop-factory_bot (2.25.0)
- rubocop (~> 1.33)
+ rubocop-factory_bot (2.25.1)
+ rubocop (~> 1.41)
rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
- rubocop-rails (2.23.1)
+ rubocop-rails (2.24.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
- rubocop-ast (>= 1.30.0, < 2.0)
- rubocop-rspec (2.26.1)
+ rubocop-ast (>= 1.31.1, < 2.0)
+ rubocop-rspec (2.27.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
@@ -735,7 +734,7 @@ GEM
stoplight (3.0.2)
redlock (~> 1.0)
stringio (3.1.0)
- strong_migrations (1.7.0)
+ strong_migrations (1.8.0)
activerecord (>= 5.2)
swd (1.3.0)
activesupport (>= 3)
@@ -866,6 +865,7 @@ DEPENDENCIES
http (~> 5.1)
http_accept_language (~> 2.1)
httplog (~> 1.6.2)
+ i18n (= 1.14.1)
i18n-tasks (~> 1.0)
idn-ruby
inline_svg
@@ -940,7 +940,7 @@ DEPENDENCIES
simplecov-lcov (~> 0.8)
stackprof
stoplight (~> 3.0.1)
- strong_migrations (= 1.7.0)
+ strong_migrations (= 1.8.0)
test-prof
thor (~> 1.2)
tty-prompt (~> 0.23)
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 9beb8fde6..d3be7817f 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -128,7 +128,7 @@ module Admin
def unblock_email
authorize @account, :unblock_email?
- CanonicalEmailBlock.where(reference_account: @account).delete_all
+ CanonicalEmailBlock.matching_account(@account).delete_all
log_action :unblock_email, @account
diff --git a/app/controllers/api/v1/featured_tags/suggestions_controller.rb b/app/controllers/api/v1/featured_tags/suggestions_controller.rb
index 76633210a..4f732ed2d 100644
--- a/app/controllers/api/v1/featured_tags/suggestions_controller.rb
+++ b/app/controllers/api/v1/featured_tags/suggestions_controller.rb
@@ -12,6 +12,10 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
private
def set_recently_used_tags
- @recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10)
+ @recently_used_tags = Tag.recently_used(current_account).where.not(id: featured_tag_ids).limit(10)
+ end
+
+ def featured_tag_ids
+ current_account.featured_tags.pluck(:tag_id)
end
end
diff --git a/app/controllers/severed_relationships_controller.rb b/app/controllers/severed_relationships_controller.rb
new file mode 100644
index 000000000..168e85e3f
--- /dev/null
+++ b/app/controllers/severed_relationships_controller.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+class SeveredRelationshipsController < ApplicationController
+ layout 'admin'
+
+ before_action :authenticate_user!
+ before_action :set_body_classes
+ before_action :set_cache_headers
+
+ before_action :set_event, only: [:following, :followers]
+
+ def index
+ @events = AccountRelationshipSeveranceEvent.where(account: current_account)
+ end
+
+ def following
+ respond_to do |format|
+ format.csv { send_data following_data, filename: "following-#{@event.target_name}-#{@event.created_at.to_date.iso8601}.csv" }
+ end
+ end
+
+ def followers
+ respond_to do |format|
+ format.csv { send_data followers_data, filename: "followers-#{@event.target_name}-#{@event.created_at.to_date.iso8601}.csv" }
+ end
+ end
+
+ private
+
+ def set_event
+ @event = AccountRelationshipSeveranceEvent.find(params[:id])
+ end
+
+ def following_data
+ CSV.generate(headers: ['Account address', 'Show boosts', 'Notify on new posts', 'Languages'], write_headers: true) do |csv|
+ @event.severed_relationships.active.about_local_account(current_account).includes(:remote_account).reorder(id: :desc).each do |follow|
+ csv << [acct(follow.target_account), follow.show_reblogs, follow.notify, follow.languages&.join(', ')]
+ end
+ end
+ end
+
+ def followers_data
+ CSV.generate(headers: ['Account address'], write_headers: true) do |csv|
+ @event.severed_relationships.passive.about_local_account(current_account).includes(:remote_account).reorder(id: :desc).each do |follow|
+ csv << [acct(follow.account)]
+ end
+ end
+ end
+
+ def acct(account)
+ account.local? ? account.local_username_and_domain : account.acct
+ end
+
+ def set_body_classes
+ @body_classes = 'admin'
+ end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
+end
diff --git a/app/helpers/branding_helper.rb b/app/helpers/branding_helper.rb
index 2b9c233c2..f72d6df5d 100644
--- a/app/helpers/branding_helper.rb
+++ b/app/helpers/branding_helper.rb
@@ -21,15 +21,4 @@ module BrandingHelper
def render_logo
image_pack_tag('logo.svg', alt: 'Mastodon', class: 'logo logo--icon')
end
-
- def render_symbol(version = :icon)
- path = case version
- when :icon
- 'logo-symbol-icon.svg'
- when :wordmark
- 'logo-symbol-wordmark.svg'
- end
-
- render(file: Rails.root.join('app', 'javascript', 'images', path)).html_safe # rubocop:disable Rails/OutputSafety
- end
end
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 12bd43f80..7477e45e5 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -75,6 +75,7 @@ export const INIT_MEDIA_EDIT_MODAL = 'INIT_MEDIA_EDIT_MODAL';
export const COMPOSE_CHANGE_MEDIA_DESCRIPTION = 'COMPOSE_CHANGE_MEDIA_DESCRIPTION';
export const COMPOSE_CHANGE_MEDIA_FOCUS = 'COMPOSE_CHANGE_MEDIA_FOCUS';
+export const COMPOSE_CHANGE_MEDIA_ORDER = 'COMPOSE_CHANGE_MEDIA_ORDER';
export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
export const COMPOSE_FOCUS = 'COMPOSE_FOCUS';
@@ -809,3 +810,9 @@ export function changePollSettings(expiresIn, isMultiple) {
isMultiple,
};
}
+
+export const changeMediaOrder = (a, b) => ({
+ type: COMPOSE_CHANGE_MEDIA_ORDER,
+ a,
+ b,
+});
diff --git a/app/javascript/mastodon/components/badge.jsx b/app/javascript/mastodon/components/badge.jsx
index 646655c24..5e0b2587b 100644
--- a/app/javascript/mastodon/components/badge.jsx
+++ b/app/javascript/mastodon/components/badge.jsx
@@ -7,8 +7,8 @@ import PersonIcon from '@/material-icons/400-24px/person.svg?react';
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
-export const Badge = ({ icon, label, domain }) => (
-
+export const Badge = ({ icon, label, domain, roleId }) => (
+
{icon}
{label}
{domain &&
{domain}}
@@ -19,6 +19,7 @@ Badge.propTypes = {
icon: PropTypes.node,
label: PropTypes.node,
domain: PropTypes.node,
+ roleId: PropTypes.string
};
Badge.defaultProps = {
diff --git a/app/javascript/mastodon/components/column_header.jsx b/app/javascript/mastodon/components/column_header.jsx
index 7fd646690..a7d07ffdb 100644
--- a/app/javascript/mastodon/components/column_header.jsx
+++ b/app/javascript/mastodon/components/column_header.jsx
@@ -13,7 +13,7 @@ import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import { Icon } from 'mastodon/components/icon';
-import { ButtonInTabsBar, useColumnsContext } from 'mastodon/features/ui/util/columns_context';
+import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { useAppHistory } from './router';
@@ -26,10 +26,9 @@ const messages = defineMessages({
back: { id: 'column_back_button.label', defaultMessage: 'Back' },
});
-const BackButton = ({ pinned, show, onlyIcon }) => {
+const BackButton = ({ onlyIcon }) => {
const history = useAppHistory();
const intl = useIntl();
- const { multiColumn } = useColumnsContext();
const handleBackClick = useCallback(() => {
if (history.location?.state?.fromMastodon) {
@@ -39,10 +38,6 @@ const BackButton = ({ pinned, show, onlyIcon }) => {
}
}, [history]);
- const showButton = history && !pinned && ((multiColumn && history.location?.state?.fromMastodon) || show);
-
- if (!showButton) return null;
-
return (