diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 04ac9560ca..f991036add 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,5 +1,5 @@
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
-FROM mcr.microsoft.com/devcontainers/ruby:0-3.2-bullseye
+FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye
# Install Rails
# RUN gem install rails webdrivers
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 4eebb74d13..a2658ea8ba 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -69,7 +69,7 @@ services:
hard: -1
libretranslate:
- image: libretranslate/libretranslate:v1.3.10
+ image: libretranslate/libretranslate:v1.3.11
restart: unless-stopped
volumes:
- lt-data:/home/libretranslate/.local
diff --git a/.eslintrc.js b/.eslintrc.js
index badcb1dcb9..206faa1c7a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -81,6 +81,15 @@ module.exports = {
{ property: 'substring', message: 'Use .slice instead of .substring.' },
{ property: 'substr', message: 'Use .slice instead of .substr.' },
],
+ 'no-restricted-syntax': [
+ 'error',
+ {
+ // eslint-disable-next-line no-restricted-syntax
+ selector: 'Literal[value=/•/], JSXText[value=/•/]',
+ // eslint-disable-next-line no-restricted-syntax
+ message: "Use '·' (middle dot) instead of '•' (bullet)",
+ },
+ ],
'no-self-assign': 'off',
'no-unused-expressions': 'error',
'no-unused-vars': 'off',
@@ -98,10 +107,17 @@ module.exports = {
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
'react/jsx-boolean-value': 'error',
'react/display-name': 'off',
+ 'react/jsx-fragments': ['error', 'syntax'],
'react/jsx-equals-spacing': 'error',
'react/jsx-no-bind': 'error',
+ 'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-target-blank': 'off',
+ 'react/jsx-tag-spacing': 'error',
+ 'react/jsx-uses-react': 'off', // not needed with new JSX transform
+ 'react/jsx-wrap-multilines': 'error',
+ 'react/no-deprecated': 'off',
'react/no-unknown-property': 'off',
+ 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error',
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/index.js
@@ -191,6 +207,55 @@ module.exports = {
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
+ 'import/order': [
+ 'error',
+ {
+ alphabetize: { order: 'asc' },
+ 'newlines-between': 'always',
+ groups: [
+ 'builtin',
+ 'external',
+ 'internal',
+ 'parent',
+ ['index', 'sibling'],
+ 'object',
+ ],
+ pathGroups: [
+ // React core packages
+ {
+ pattern: '{react,react-dom,react-dom/client,prop-types}',
+ group: 'builtin',
+ position: 'after',
+ },
+ // I18n
+ {
+ pattern: '{react-intl,intl-messageformat}',
+ group: 'builtin',
+ position: 'after',
+ },
+ // Common React utilities
+ {
+ pattern: '{classnames,react-helmet,react-router-dom}',
+ group: 'external',
+ position: 'before',
+ },
+ // Immutable / Redux / data store
+ {
+ pattern: '{immutable,react-redux,react-immutable-proptypes,react-immutable-pure-component,reselect}',
+ group: 'external',
+ position: 'before',
+ },
+ // Internal packages
+ {
+ pattern: '{mastodon/**,flavours/glitch-soc/**}',
+ group: 'internal',
+ position: 'after',
+ },
+ ],
+ pathGroupsExcludedImportTypes: [],
+ },
+ ],
+
'promise/always-return': 'off',
'promise/catch-or-return': [
'error',
@@ -237,6 +302,7 @@ module.exports = {
'.*rc.js',
'ide-helper.js',
'config/webpack/**/*',
+ 'config/formatjs-formatter.js',
],
env: {
@@ -267,7 +333,7 @@ module.exports = {
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:promise/recommended',
- 'plugin:jsdoc/recommended',
+ 'plugin:jsdoc/recommended-typescript',
'plugin:prettier/recommended',
],
@@ -279,55 +345,6 @@ module.exports = {
rules: {
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
- 'import/order': [
- 'error',
- {
- alphabetize: { order: 'asc' },
- 'newlines-between': 'always',
- groups: [
- 'builtin',
- 'external',
- 'internal',
- 'parent',
- ['index', 'sibling'],
- 'object',
- ],
- pathGroups: [
- // React core packages
- {
- pattern: '{react,react-dom,prop-types}',
- group: 'builtin',
- position: 'after',
- },
- // I18n
- {
- pattern: 'react-intl',
- group: 'builtin',
- position: 'after',
- },
- // Common React utilities
- {
- pattern: '{classnames,react-helmet}',
- group: 'external',
- position: 'before',
- },
- // Immutable / Redux / data store
- {
- pattern: '{immutable,react-redux,react-immutable-proptypes,react-immutable-pure-component,reselect}',
- group: 'external',
- position: 'before',
- },
- // Internal packages
- {
- pattern: '{mastodon/**,flavours/glitch-soc/**}',
- group: 'internal',
- position: 'after',
- },
- ],
- pathGroupsExcludedImportTypes: [],
- },
- ],
-
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
@@ -339,6 +356,9 @@ module.exports = {
'import/no-default-export': 'warn',
'react/prefer-stateless-function': 'warn',
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
+ 'react/jsx-uses-react': 'off', // not needed with new JSX transform
+ 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
+ 'react/prop-types': 'off',
},
},
{
diff --git a/.github/renovate.json5 b/.github/renovate.json5
new file mode 100644
index 0000000000..1ae40d4161
--- /dev/null
+++ b/.github/renovate.json5
@@ -0,0 +1,114 @@
+{
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
+ extends: [
+ 'config:base',
+ ':dependencyDashboard',
+ ':labels(dependencies)',
+ ':maintainLockFilesMonthly', // update non-direct dependencies monthly
+ ':prConcurrentLimit10', // only 10 open PRs at the same time
+ ],
+ stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
+ // packageRules order is important, they are applied from top to bottom and are merged,
+ // so for example grouping rules needs to be at the bottom
+ packageRules: [
+ {
+ // Ignore major version bumps for these node packages
+ matchManagers: ['npm'],
+ matchPackageNames: [
+ '@rails/ujs', // Needs to match the major Rails version
+ 'tesseract.js', // Requires code changes
+ 'react-hotkeys', // Requires code changes
+
+ // Requires Webpacker upgrade or replacement
+ '@types/webpack',
+ 'babel-loader',
+ 'compression-webpack-plugin',
+ 'css-loader',
+ 'imports-loader',
+ 'mini-css-extract-plugin',
+ 'postcss-loader',
+ 'sass-loader',
+ 'terser-webpack-plugin',
+ 'webpack',
+ 'webpack-assets-manifest',
+ 'webpack-bundle-analyzer',
+ 'webpack-dev-server',
+ 'webpack-cli',
+
+ // react-router: Requires manual upgrade
+ 'history',
+ 'react-router-dom',
+ ],
+ matchUpdateTypes: ['major'],
+ enabled: false,
+ },
+ {
+ // Ignore major version bumps for these Ruby packages
+ matchManagers: ['bundler'],
+ matchPackageNames: [
+ 'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
+ 'strong_migrations', // Requires manual upgrade
+ 'sidekiq', // Requires manual upgrade
+ 'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version
+ 'redis', // Requires manual upgrade and sync with Sidekiq version
+ 'fog-openstack', // TODO: was ignored in https://github.com/mastodon/mastodon/pull/13964
+
+ // Needs major Rails version bump
+ 'rack',
+ 'rails',
+ 'rails-i18n',
+ ],
+ matchUpdateTypes: ['major'],
+ enabled: false,
+ },
+ {
+ // Update Github Actions and Docker images weekly
+ matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
+ extends: ['schedule:weekly'],
+ },
+ {
+ // Ignore major & minor bumps for the ruby image, this needs to be synced with .ruby-version
+ matchManagers: ['dockerfile'],
+ matchPackageNames: ['moritzheiber/ruby-jemalloc'],
+ matchUpdateTypes: ['minor', 'major'],
+ enabled: false,
+ },
+ {
+ // Ignore major bump for the node image, this needs to be synced with .nvmrc
+ matchManagers: ['dockerfile'],
+ matchPackageNames: ['node'],
+ matchUpdateTypes: ['major'],
+ enabled: false,
+ },
+ {
+ // Ignore major postgres bumps in the docker-compose file, as those break dev environments
+ matchManagers: ['docker-compose'],
+ matchPackageNames: ['postgres'],
+ matchUpdateTypes: ['major'],
+ enabled: false,
+ },
+ {
+ // Update devDependencies every week, with one grouped PR
+ matchDepTypes: 'devDependencies',
+ matchUpdateTypes: ['patch', 'minor'],
+ excludePackageNames: [
+ 'typescript', // Typescript has many changes in minor versions, needs to be checked every time
+ ],
+ groupName: 'devDependencies (non-major)',
+ extends: ['schedule:weekly'],
+ },
+ {
+ // Update @types/* packages every week, with one grouped PR
+ matchPackagePrefixes: '@types/',
+ matchUpdateTypes: ['patch', 'minor'],
+ groupName: 'DefinitelyTyped types (non-major)',
+ extends: ['schedule:weekly'],
+ addLabels: ['typescript'],
+ },
+ // Add labels depending on package manager
+ { matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
+ { matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
+ { matchManagers: ['docker-compose', 'dockerfile'], addLabels: ['docker'] },
+ { matchManagers: ['github-actions'], addLabels: ['github_actions'] },
+ ],
+}
diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml
index e282e2ab72..b67c503e95 100644
--- a/.github/workflows/check-i18n.yml
+++ b/.github/workflows/check-i18n.yml
@@ -41,8 +41,7 @@ jobs:
- name: Check for missing strings in English JSON
run: |
- yarn build:development
- yarn manage:translations en
+ yarn i18n:extract --throws
git diff --exit-code
- name: Check locale file normalization
diff --git a/.github/workflows/lint-css.yml b/.github/workflows/lint-css.yml
index e13d227bdb..4d3c2ce5af 100644
--- a/.github/workflows/lint-css.yml
+++ b/.github/workflows/lint-css.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
@@ -48,4 +49,4 @@ jobs:
- run: echo "::add-matcher::.github/stylelint-matcher.json"
- name: Stylelint
- run: yarn test:lint:sass
+ run: yarn lint:sass
diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml
index 2ddbca7818..56d817123a 100644
--- a/.github/workflows/lint-haml.yml
+++ b/.github/workflows/lint-haml.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- '.github/workflows/haml-lint-problem-matcher.json'
- '.github/workflows/lint-haml.yml'
diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml
index 7700e48512..1f0cfd1e70 100644
--- a/.github/workflows/lint-js.yml
+++ b/.github/workflows/lint-js.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
@@ -48,7 +49,7 @@ jobs:
run: yarn --frozen-lockfile
- name: ESLint
- run: yarn test:lint:js --max-warnings 0
+ run: yarn lint:js --max-warnings 0
- name: Typecheck
- run: yarn test:typecheck
+ run: yarn typecheck
diff --git a/.github/workflows/lint-json.yml b/.github/workflows/lint-json.yml
index 98f101ad95..8712d8bd80 100644
--- a/.github/workflows/lint-json.yml
+++ b/.github/workflows/lint-json.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
@@ -40,4 +41,4 @@ jobs:
run: yarn --frozen-lockfile
- name: Prettier
- run: yarn prettier --check "**/*.json"
+ run: yarn lint:json
diff --git a/.github/workflows/lint-md.yml b/.github/workflows/lint-md.yml
index 6f76dd60c2..d19a0470db 100644
--- a/.github/workflows/lint-md.yml
+++ b/.github/workflows/lint-md.yml
@@ -3,8 +3,10 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- '.github/workflows/lint-md.yml'
+ - '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
@@ -14,6 +16,7 @@ on:
pull_request:
paths:
- '.github/workflows/lint-md.yml'
+ - '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
@@ -32,9 +35,10 @@ jobs:
uses: actions/setup-node@v3
with:
cache: yarn
+ node-version-file: '.nvmrc'
- name: Install all yarn packages
run: yarn --frozen-lockfile
- name: Prettier
- run: yarn prettier --check "**/*.md"
+ run: yarn lint:md
diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml
index de54fe9ae5..0395c8639f 100644
--- a/.github/workflows/lint-ruby.yml
+++ b/.github/workflows/lint-ruby.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'Gemfile*'
- '.rubocop*.yml'
diff --git a/.github/workflows/lint-yml.yml b/.github/workflows/lint-yml.yml
index 6f79babcfd..295e9610b3 100644
--- a/.github/workflows/lint-yml.yml
+++ b/.github/workflows/lint-yml.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
@@ -42,4 +43,4 @@ jobs:
run: yarn --frozen-lockfile
- name: Prettier
- run: yarn prettier --check "**/*.{yml,yaml}"
+ run: yarn lint:yml
diff --git a/.github/workflows/rebase-needed.yml b/.github/workflows/rebase-needed.yml
index 6a8035210c..131a62a576 100644
--- a/.github/workflows/rebase-needed.yml
+++ b/.github/workflows/rebase-needed.yml
@@ -4,10 +4,12 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
- 'l10n_main'
pull_request_target:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
- 'l10n_main'
types: [synchronize]
diff --git a/.github/workflows/test-js.yml b/.github/workflows/test-js.yml
index 1c4958550e..3306105f9e 100644
--- a/.github/workflows/test-js.yml
+++ b/.github/workflows/test-js.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
@@ -44,4 +45,4 @@ jobs:
run: yarn --frozen-lockfile
- name: Jest testing
- run: yarn test:jest --reporters github-actions summary
+ run: yarn jest --reporters github-actions summary
diff --git a/.github/workflows/test-migrations-one-step.yml b/.github/workflows/test-migrations-one-step.yml
index 212b2cfe73..a91fd819a2 100644
--- a/.github/workflows/test-migrations-one-step.yml
+++ b/.github/workflows/test-migrations-one-step.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
pull_request:
jobs:
diff --git a/.github/workflows/test-migrations-two-step.yml b/.github/workflows/test-migrations-two-step.yml
index 310153929d..50266fb8a0 100644
--- a/.github/workflows/test-migrations-two-step.yml
+++ b/.github/workflows/test-migrations-two-step.yml
@@ -3,6 +3,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
pull_request:
jobs:
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index f284745ea4..07cb1d41f8 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -4,6 +4,7 @@ on:
push:
branches-ignore:
- 'dependabot/**'
+ - 'renovate/**'
pull_request:
env:
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 12ca463422..d1ed30b260 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -4,6 +4,11 @@ exclude:
- 'vendor/**/*'
- lib/templates/haml/scaffold/_form.html.haml
+require:
+ - ./lib/linter/haml_middle_dot.rb
+
linters:
AltText:
enabled: true
+ MiddleDot:
+ enabled: true
diff --git a/.prettierignore b/.prettierignore
index af0411e9cc..27b6d5458a 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -61,7 +61,7 @@ docker-compose.override.yml
/app/javascript/mastodon/features/emoji/emoji_map.json
# Ignore locale files
-/app/javascript/mastodon/locales
+/app/javascript/mastodon/locales/*.json
/config/locales
# Ignore vendored CSS reset
diff --git a/.rubocop.yml b/.rubocop.yml
index ceade6e582..eff89bdaee 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -11,6 +11,7 @@ require:
- rubocop-rspec
- rubocop-performance
- rubocop-capybara
+ - ./lib/linter/rubocop_middle_dot
AllCops:
TargetRubyVersion: 3.0 # Set to minimum supported version of CI
@@ -43,7 +44,7 @@ Layout/LineLength:
- !ruby/regexp / \# .*$/
- !ruby/regexp /^\# .*$/
Exclude:
- - lib/**/*cli*.rb
+ - 'lib/mastodon/cli/*.rb'
- db/*migrate/**/*
- db/seeds/**/*
@@ -53,113 +54,52 @@ Lint/UselessAccessModifier:
ContextCreatingMethods:
- class_methods
+## Disable most Metrics/*Length cops
+# Reason: those are often triggered and force significant refactors when this happend
+# but the team feel they are not really improving the code quality.
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
+Metrics/BlockLength:
+ Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
+Metrics/ClassLength:
+ Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
+Metrics/MethodLength:
+ Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
+Metrics/ModuleLength:
+ Enabled: false
+
+## End Disable Metrics/*Length cops
+
# Reason: Currently disabled in .rubocop_todo.yml
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
Metrics/AbcSize:
Exclude:
- - 'lib/**/*cli*.rb'
+ - 'lib/mastodon/cli/*.rb'
- db/*migrate/**/*
-# Reason: Some functions cannot be broken up, but others may be refactor candidates
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
-Metrics/BlockLength:
- CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
- Exclude:
- - 'config/routes.rb'
- - 'lib/mastodon/*_cli.rb'
- - 'lib/tasks/*.rake'
- - 'app/models/concerns/account_associations.rb'
- - 'app/models/concerns/account_interactions.rb'
- - 'app/models/concerns/ldap_authenticable.rb'
- - 'app/models/concerns/omniauthable.rb'
- - 'app/models/concerns/pam_authenticable.rb'
- - 'app/models/concerns/remotable.rb'
- - 'app/services/suspend_account_service.rb'
- - 'app/services/unsuspend_account_service.rb'
- - 'app/views/accounts/show.rss.ruby'
- - 'app/views/tags/show.rss.ruby'
- - 'config/environments/development.rb'
- - 'config/environments/production.rb'
- - 'config/initializers/devise.rb'
- - 'config/initializers/doorkeeper.rb'
- - 'config/initializers/omniauth.rb'
- - 'config/initializers/simple_form.rb'
- - 'config/navigation.rb'
- - 'config/routes.rb'
- - 'config/routes/*.rb'
- - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
- - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
- - 'lib/paperclip/gif_transcoder.rb'
-
# Reason:
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting
Metrics/BlockNesting:
Exclude:
- - 'lib/mastodon/*_cli.rb'
-
-# Reason: Some Excluded files would be candidates for refactoring but not currently addressed
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
-Metrics/ClassLength:
- CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
- Exclude:
- - 'lib/mastodon/*_cli.rb'
- - 'app/controllers/admin/accounts_controller.rb'
- - 'app/controllers/api/base_controller.rb'
- - 'app/controllers/api/v1/admin/accounts_controller.rb'
- - 'app/controllers/application_controller.rb'
- - 'app/controllers/auth/registrations_controller.rb'
- - 'app/controllers/auth/sessions_controller.rb'
- - 'app/lib/activitypub/activity.rb'
- - 'app/lib/activitypub/activity/create.rb'
- - 'app/lib/activitypub/tag_manager.rb'
- - 'app/lib/feed_manager.rb'
- - 'app/lib/link_details_extractor.rb'
- - 'app/lib/request.rb'
- - 'app/lib/text_formatter.rb'
- - 'app/lib/user_settings_decorator.rb'
- - 'app/mailers/user_mailer.rb'
- - 'app/models/account.rb'
- - 'app/models/admin/account_action.rb'
- - 'app/models/form/account_batch.rb'
- - 'app/models/media_attachment.rb'
- - 'app/models/status.rb'
- - 'app/models/tag.rb'
- - 'app/models/user.rb'
- - 'app/serializers/activitypub/actor_serializer.rb'
- - 'app/serializers/activitypub/note_serializer.rb'
- - 'app/serializers/rest/status_serializer.rb'
- - 'app/services/account_search_service.rb'
- - 'app/services/activitypub/process_account_service.rb'
- - 'app/services/activitypub/process_status_update_service.rb'
- - 'app/services/backup_service.rb'
- - 'app/services/bulk_import_service.rb'
- - 'app/services/delete_account_service.rb'
- - 'app/services/fan_out_on_write_service.rb'
- - 'app/services/fetch_link_card_service.rb'
- - 'app/services/import_service.rb'
- - 'app/services/notify_service.rb'
- - 'app/services/post_status_service.rb'
- - 'app/services/update_status_service.rb'
- - 'lib/paperclip/color_extractor.rb'
+ - 'lib/mastodon/cli/*.rb'
# Reason: Currently disabled in .rubocop_todo.yml
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
Metrics/CyclomaticComplexity:
Exclude:
- - lib/mastodon/*cli*.rb
+ - lib/mastodon/cli/*.rb
- db/*migrate/**/*
-# Reason: Currently disabled in .rubocop_todo.yml
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
-Metrics/MethodLength:
- CountAsOne: [array, heredoc]
- Exclude:
- - 'lib/mastodon/*_cli.rb'
-
# Reason:
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
-Metrics/ModuleLength:
- CountAsOne: [array, heredoc]
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists
+Metrics/ParameterLists:
+ CountKeywordArgs: false
# Reason: Prevailing style is argument file paths
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
@@ -171,13 +111,12 @@ Rails/FilePath:
Rails/HttpStatus:
EnforcedStyle: numeric
-# Reason: Allowed only in the `tootctl` CLI application code
+# Reason: Allowed in `tootctl` CLI code and in boot ENV checker
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
Rails/Exit:
Exclude:
- - 'lib/mastodon/*_cli.rb'
- - 'lib/mastodon/cli_helper.rb'
- - 'lib/cli.rb'
+ - 'config/boot.rb'
+ - 'lib/mastodon/cli/*.rb'
# Reason: Some single letter camel case files shouldn't be split
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
@@ -267,3 +206,6 @@ Style/TrailingCommaInArrayLiteral:
# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: 'comma'
+
+Style/MiddleDot:
+ Enabled: true
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 77caad4924..c1d580e515 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
-# using RuboCop version 1.50.2.
+# using RuboCop version 1.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@@ -48,18 +48,14 @@ Layout/SpaceInLambdaLiteral:
- 'config/environments/production.rb'
- 'config/initializers/content_security_policy.rb'
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedMethods, AllowedPatterns.
Lint/AmbiguousBlockAssociation:
Exclude:
- - 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/otp_authentication_controller_spec.rb'
- - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
- 'spec/services/activitypub/process_status_update_service_spec.rb'
- 'spec/services/post_status_service_spec.rb'
- - 'spec/services/suspend_account_service_spec.rb'
- - 'spec/services/unsuspend_account_service_spec.rb'
- - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
# Configuration parameters: AllowComments, AllowEmptyLambdas.
Lint/EmptyBlock:
@@ -124,6 +120,7 @@ Lint/UnusedBlockArgument:
- 'config/initializers/paperclip.rb'
- 'config/initializers/simple_form.rb'
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/UselessAssignment:
Exclude:
- 'app/services/activitypub/process_status_update_service.rb'
@@ -145,6 +142,7 @@ Lint/UselessAssignment:
- 'spec/services/resolve_url_service_spec.rb'
- 'spec/views/statuses/show.html.haml_spec.rb'
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
@@ -156,12 +154,6 @@ Metrics/AbcSize:
Exclude:
- 'app/serializers/initial_state_serializer.rb'
-# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
-# AllowedMethods: refine
-Metrics/BlockLength:
- Exclude:
- - 'app/models/concerns/status_safe_reblog_insert.rb'
-
# Configuration parameters: CountBlocks, Max.
Metrics/BlockNesting:
Exclude:
@@ -171,31 +163,9 @@ Metrics/BlockNesting:
Metrics/CyclomaticComplexity:
Max: 25
-# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
-Metrics/MethodLength:
- Max: 58
-
-# Configuration parameters: CountComments, Max, CountAsOne.
-Metrics/ModuleLength:
- Exclude:
- - 'app/controllers/concerns/signature_verification.rb'
- - 'app/helpers/application_helper.rb'
- - 'app/helpers/jsonld_helper.rb'
- - 'app/models/concerns/account_interactions.rb'
- - 'app/models/concerns/has_user_settings.rb'
- - 'lib/sanitize_ext/sanitize_config.rb'
-
-# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
-Metrics/ParameterLists:
- Exclude:
- - 'app/models/concerns/account_interactions.rb'
- - 'app/services/activitypub/fetch_remote_account_service.rb'
- - 'app/services/activitypub/fetch_remote_actor_service.rb'
- - 'app/services/activitypub/fetch_remote_status_service.rb'
-
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
- Max: 28
+ Max: 27
Naming/AccessorMethodName:
Exclude:
@@ -208,6 +178,7 @@ Naming/FileName:
Exclude:
- 'config/locales/sr-Latn.rb'
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
Naming/MemoizedInstanceVariableName:
@@ -230,14 +201,9 @@ Naming/VariableNumber:
- 'db/migrate/20190820003045_update_statuses_index.rb'
- 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
- 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
- - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
- - 'spec/lib/feed_manager_spec.rb'
- 'spec/models/account_spec.rb'
- - 'spec/models/concerns/account_interactions_spec.rb'
- - 'spec/models/custom_emoji_filter_spec.rb'
- 'spec/models/domain_block_spec.rb'
- 'spec/models/user_spec.rb'
- - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Performance/UnfreezeString:
@@ -267,79 +233,6 @@ RSpec/AnyInstance:
- 'spec/workers/activitypub/delivery_worker_spec.rb'
- 'spec/workers/web/push_notification_worker_spec.rb'
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: SkipBlocks, EnforcedStyle.
-# SupportedStyles: described_class, explicit
-RSpec/DescribedClass:
- Exclude:
- - 'spec/controllers/concerns/cache_concern_spec.rb'
- - 'spec/controllers/concerns/challengable_concern_spec.rb'
- - 'spec/lib/entity_cache_spec.rb'
- - 'spec/lib/extractor_spec.rb'
- - 'spec/lib/feed_manager_spec.rb'
- - 'spec/lib/hash_object_spec.rb'
- - 'spec/lib/ostatus/tag_manager_spec.rb'
- - 'spec/lib/request_spec.rb'
- - 'spec/lib/tag_manager_spec.rb'
- - 'spec/lib/webfinger_resource_spec.rb'
- - 'spec/mailers/notification_mailer_spec.rb'
- - 'spec/mailers/user_mailer_spec.rb'
- - 'spec/models/account_conversation_spec.rb'
- - 'spec/models/account_domain_block_spec.rb'
- - 'spec/models/account_migration_spec.rb'
- - 'spec/models/account_spec.rb'
- - 'spec/models/block_spec.rb'
- - 'spec/models/domain_block_spec.rb'
- - 'spec/models/email_domain_block_spec.rb'
- - 'spec/models/export_spec.rb'
- - 'spec/models/favourite_spec.rb'
- - 'spec/models/follow_spec.rb'
- - 'spec/models/identity_spec.rb'
- - 'spec/models/import_spec.rb'
- - 'spec/models/media_attachment_spec.rb'
- - 'spec/models/notification_spec.rb'
- - 'spec/models/relationship_filter_spec.rb'
- - 'spec/models/report_filter_spec.rb'
- - 'spec/models/session_activation_spec.rb'
- - 'spec/models/setting_spec.rb'
- - 'spec/models/site_upload_spec.rb'
- - 'spec/models/status_pin_spec.rb'
- - 'spec/models/status_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/policies/account_moderation_note_policy_spec.rb'
- - 'spec/presenters/account_relationships_presenter_spec.rb'
- - 'spec/presenters/status_relationships_presenter_spec.rb'
- - 'spec/serializers/activitypub/note_serializer_spec.rb'
- - 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
- - 'spec/serializers/rest/account_serializer_spec.rb'
- - 'spec/services/activitypub/fetch_remote_account_service_spec.rb'
- - 'spec/services/activitypub/fetch_remote_actor_service_spec.rb'
- - 'spec/services/activitypub/fetch_remote_key_service_spec.rb'
- - 'spec/services/after_block_domain_from_account_service_spec.rb'
- - 'spec/services/authorize_follow_service_spec.rb'
- - 'spec/services/batched_remove_status_service_spec.rb'
- - 'spec/services/block_domain_service_spec.rb'
- - 'spec/services/block_service_spec.rb'
- - 'spec/services/bootstrap_timeline_service_spec.rb'
- - 'spec/services/clear_domain_media_service_spec.rb'
- - 'spec/services/favourite_service_spec.rb'
- - 'spec/services/follow_service_spec.rb'
- - 'spec/services/import_service_spec.rb'
- - 'spec/services/post_status_service_spec.rb'
- - 'spec/services/precompute_feed_service_spec.rb'
- - 'spec/services/process_mentions_service_spec.rb'
- - 'spec/services/purge_domain_service_spec.rb'
- - 'spec/services/reblog_service_spec.rb'
- - 'spec/services/reject_follow_service_spec.rb'
- - 'spec/services/remove_from_followers_service_spec.rb'
- - 'spec/services/remove_status_service_spec.rb'
- - 'spec/services/unallow_domain_service_spec.rb'
- - 'spec/services/unblock_service_spec.rb'
- - 'spec/services/unfollow_service_spec.rb'
- - 'spec/services/unmute_service_spec.rb'
- - 'spec/services/update_account_service_spec.rb'
- - 'spec/validators/note_length_validator_spec.rb'
-
# This cop supports unsafe autocorrection (--autocorrect-all).
RSpec/EmptyExampleGroup:
Exclude:
@@ -379,29 +272,6 @@ RSpec/EmptyExampleGroup:
RSpec/ExampleLength:
Max: 22
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: method_call, block
-RSpec/ExpectChange:
- Exclude:
- - 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
- - 'spec/controllers/admin/custom_emojis_controller_spec.rb'
- - 'spec/controllers/admin/invites_controller_spec.rb'
- - 'spec/controllers/admin/report_notes_controller_spec.rb'
- - 'spec/controllers/concerns/accountable_concern_spec.rb'
- - 'spec/controllers/invites_controller_spec.rb'
- - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
- - 'spec/models/admin/account_action_spec.rb'
- - 'spec/services/suspend_account_service_spec.rb'
- - 'spec/services/unsuspend_account_service_spec.rb'
- - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
-
-RSpec/ExpectInHook:
- Exclude:
- - 'spec/controllers/api/v1/media_controller_spec.rb'
- - 'spec/controllers/settings/applications_controller_spec.rb'
- - 'spec/lib/status_filter_spec.rb'
-
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: implicit, each, example
@@ -446,11 +316,8 @@ RSpec/LetSetup:
- 'spec/controllers/admin/statuses_controller_spec.rb'
- 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
- 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
- - 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
- - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
- 'spec/controllers/api/v1/filters_controller_spec.rb'
- 'spec/controllers/api/v1/followed_tags_controller_spec.rb'
- - 'spec/controllers/api/v1/tags_controller_spec.rb'
- 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
- 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
- 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
@@ -487,7 +354,6 @@ RSpec/LetSetup:
- 'spec/services/suspend_account_service_spec.rb'
- 'spec/services/unallow_domain_service_spec.rb'
- 'spec/services/unsuspend_account_service_spec.rb'
- - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
- 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb'
RSpec/MessageChain:
@@ -519,32 +385,8 @@ RSpec/MessageSpies:
- 'spec/spec_helper.rb'
- 'spec/validators/status_length_validator_spec.rb'
-RSpec/MissingExampleGroupArgument:
- Exclude:
- - 'spec/controllers/accounts_controller_spec.rb'
- - 'spec/controllers/activitypub/collections_controller_spec.rb'
- - 'spec/controllers/admin/statuses_controller_spec.rb'
- - 'spec/controllers/admin/users/roles_controller_spec.rb'
- - 'spec/controllers/api/v1/accounts_controller_spec.rb'
- - 'spec/controllers/api/v1/admin/account_actions_controller_spec.rb'
- - 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
- - 'spec/controllers/api/v1/statuses_controller_spec.rb'
- - 'spec/controllers/auth/registrations_controller_spec.rb'
- - 'spec/features/log_in_spec.rb'
- - 'spec/lib/activitypub/activity/undo_spec.rb'
- - 'spec/lib/status_reach_finder_spec.rb'
- - 'spec/models/account_spec.rb'
- - 'spec/models/email_domain_block_spec.rb'
- - 'spec/models/trends/statuses_spec.rb'
- - 'spec/models/trends/tags_spec.rb'
- - 'spec/models/user_role_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/services/fetch_link_card_service_spec.rb'
- - 'spec/services/notify_service_spec.rb'
- - 'spec/services/process_mentions_service_spec.rb'
-
RSpec/MultipleExpectations:
- Max: 19
+ Max: 8
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
@@ -554,26 +396,10 @@ RSpec/MultipleMemoizedHelpers:
RSpec/NestedGroups:
Max: 6
-# Configuration parameters: AllowedPatterns.
-# AllowedPatterns: ^expect_, ^assert_
-RSpec/NoExpectationExample:
- Exclude:
- - 'spec/controllers/auth/registrations_controller_spec.rb'
- - 'spec/services/precompute_feed_service_spec.rb'
-
RSpec/PendingWithoutReason:
Exclude:
- 'spec/models/account_spec.rb'
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
-# SupportedStyles: inflected, explicit
-RSpec/PredicateMatcher:
- Exclude:
- - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/services/post_status_service_spec.rb'
-
RSpec/StubbedMock:
Exclude:
- 'spec/controllers/api/base_controller_spec.rb'
@@ -588,7 +414,6 @@ RSpec/StubbedMock:
RSpec/SubjectDeclaration:
Exclude:
- 'spec/controllers/admin/domain_blocks_controller_spec.rb'
- - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
- 'spec/models/account_migration_spec.rb'
- 'spec/models/account_spec.rb'
- 'spec/models/relationship_filter_spec.rb'
@@ -614,45 +439,6 @@ RSpec/SubjectStub:
- 'spec/services/unallow_domain_service_spec.rb'
- 'spec/validators/blacklisted_email_validator_spec.rb'
-# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
-RSpec/VerifiedDoubles:
- Exclude:
- - 'spec/controllers/admin/change_emails_controller_spec.rb'
- - 'spec/controllers/admin/confirmations_controller_spec.rb'
- - 'spec/controllers/admin/disputes/appeals_controller_spec.rb'
- - 'spec/controllers/admin/domain_allows_controller_spec.rb'
- - 'spec/controllers/admin/domain_blocks_controller_spec.rb'
- - 'spec/controllers/api/v1/reports_controller_spec.rb'
- - 'spec/controllers/api/web/embeds_controller_spec.rb'
- - 'spec/controllers/auth/sessions_controller_spec.rb'
- - 'spec/controllers/disputes/appeals_controller_spec.rb'
- - 'spec/helpers/statuses_helper_spec.rb'
- - 'spec/lib/suspicious_sign_in_detector_spec.rb'
- - 'spec/models/account/field_spec.rb'
- - 'spec/models/session_activation_spec.rb'
- - 'spec/models/setting_spec.rb'
- - 'spec/services/account_search_service_spec.rb'
- - 'spec/services/post_status_service_spec.rb'
- - 'spec/services/search_service_spec.rb'
- - 'spec/validators/blacklisted_email_validator_spec.rb'
- - 'spec/validators/disallowed_hashtags_validator_spec.rb'
- - 'spec/validators/email_mx_validator_spec.rb'
- - 'spec/validators/follow_limit_validator_spec.rb'
- - 'spec/validators/note_length_validator_spec.rb'
- - 'spec/validators/poll_validator_spec.rb'
- - 'spec/validators/status_length_validator_spec.rb'
- - 'spec/validators/status_pin_validator_spec.rb'
- - 'spec/validators/unique_username_validator_spec.rb'
- - 'spec/validators/unreserved_username_validator_spec.rb'
- - 'spec/validators/url_validator_spec.rb'
- - 'spec/views/statuses/show.html.haml_spec.rb'
- - 'spec/workers/activitypub/processing_worker_spec.rb'
- - 'spec/workers/admin/domain_purge_worker_spec.rb'
- - 'spec/workers/domain_block_worker_spec.rb'
- - 'spec/workers/domain_clear_media_worker_spec.rb'
- - 'spec/workers/feed_insert_worker_spec.rb'
- - 'spec/workers/regeneration_worker_spec.rb'
-
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/ApplicationController:
Exclude:
@@ -715,12 +501,6 @@ Rails/DuplicateAssociation:
- 'app/serializers/activitypub/collection_serializer.rb'
- 'app/serializers/activitypub/note_serializer.rb'
-# Configuration parameters: Include.
-# Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb
-Rails/Exit:
- Exclude:
- - 'config/boot.rb'
-
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany:
@@ -769,7 +549,6 @@ Rails/NegateInclude:
- 'app/models/concerns/attachmentable.rb'
- 'app/models/concerns/remotable.rb'
- 'app/models/custom_filter.rb'
- - 'app/models/webhook.rb'
- 'app/services/activitypub/process_status_update_service.rb'
- 'app/services/fetch_link_card_service.rb'
- 'app/services/search_service.rb'
@@ -855,9 +634,9 @@ Rails/SkipsModelValidations:
- 'db/post_migrate/20220617202502_migrate_roles.rb'
- 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
- 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
- - 'lib/cli.rb'
- - 'lib/mastodon/accounts_cli.rb'
- - 'lib/mastodon/maintenance_cli.rb'
+ - 'lib/mastodon/cli/accounts.rb'
+ - 'lib/mastodon/cli/main.rb'
+ - 'lib/mastodon/cli/maintenance.rb'
- 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
- 'spec/lib/activitypub/activity/follow_spec.rb'
- 'spec/services/follow_service_spec.rb'
@@ -939,13 +718,9 @@ Rails/WhereExists:
- 'app/validators/vote_validator.rb'
- 'app/workers/move_worker.rb'
- 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
- - 'lib/mastodon/email_domain_blocks_cli.rb'
- 'lib/tasks/tests.rake'
- - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
- - 'spec/controllers/api/v1/tags_controller_spec.rb'
- 'spec/models/account_spec.rb'
- 'spec/services/activitypub/process_collection_service_spec.rb'
- - 'spec/services/post_status_service_spec.rb'
- 'spec/services/purge_domain_service_spec.rb'
- 'spec/services/unallow_domain_service_spec.rb'
@@ -967,6 +742,7 @@ Style/ClassVars:
Exclude:
- 'config/initializers/devise.rb'
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/CombinableLoops:
Exclude:
- 'app/models/form/custom_emoji_batch.rb'
@@ -1001,7 +777,6 @@ Style/FormatStringToken:
Exclude:
- 'app/models/privacy_policy.rb'
- 'config/initializers/devise.rb'
- - 'lib/mastodon/maintenance_cli.rb'
- 'lib/paperclip/color_extractor.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -1411,11 +1186,6 @@ Style/GlobalStdStream:
- 'config/environments/development.rb'
- 'config/environments/production.rb'
-# Configuration parameters: AllowedVariables.
-Style/GlobalVars:
- Exclude:
- - 'config/initializers/statsd.rb'
-
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
@@ -1446,9 +1216,9 @@ Style/GuardClause:
- 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb'
- 'lib/devise/two_factor_ldap_authenticatable.rb'
- 'lib/devise/two_factor_pam_authenticatable.rb'
- - 'lib/mastodon/accounts_cli.rb'
- - 'lib/mastodon/maintenance_cli.rb'
- - 'lib/mastodon/media_cli.rb'
+ - 'lib/mastodon/cli/accounts.rb'
+ - 'lib/mastodon/cli/maintenance.rb'
+ - 'lib/mastodon/cli/media.rb'
- 'lib/paperclip/attachment_extensions.rb'
- 'lib/tasks/repo.rake'
@@ -1549,7 +1319,6 @@ Style/RedundantConstantBase:
Exclude:
- 'config/environments/production.rb'
- 'config/initializers/sidekiq.rb'
- - 'config/initializers/statsd.rb'
- 'config/locales/sr-Latn.rb'
- 'config/locales/sr.rb'
@@ -1563,52 +1332,6 @@ Style/RedundantFetchBlock:
- 'config/initializers/paperclip.rb'
- 'config/puma.rb'
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantRegexpCharacterClass:
- Exclude:
- - 'app/lib/link_details_extractor.rb'
- - 'app/lib/tag_manager.rb'
- - 'app/models/domain_allow.rb'
- - 'app/models/domain_block.rb'
- - 'app/services/fetch_oembed_service.rb'
- - 'config/initializers/rack_attack.rb'
- - 'lib/tasks/emojis.rake'
- - 'lib/tasks/mastodon.rake'
-
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantRegexpEscape:
- Exclude:
- - 'app/lib/webfinger_resource.rb'
- - 'app/models/account.rb'
- - 'app/models/tag.rb'
- - 'app/services/fetch_link_card_service.rb'
- - 'config/initializers/twitter_regex.rb'
- - 'lib/paperclip/color_extractor.rb'
- - 'lib/tasks/mastodon.rake'
-
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
-# SupportedStyles: slashes, percent_r, mixed
-Style/RegexpLiteral:
- Exclude:
- - 'app/lib/link_details_extractor.rb'
- - 'app/lib/plain_text_formatter.rb'
- - 'app/lib/tag_manager.rb'
- - 'app/lib/text_formatter.rb'
- - 'app/models/account.rb'
- - 'app/models/domain_allow.rb'
- - 'app/models/domain_block.rb'
- - 'app/models/site_upload.rb'
- - 'app/models/tag.rb'
- - 'app/services/backup_service.rb'
- - 'app/services/fetch_oembed_service.rb'
- - 'app/services/search_service.rb'
- - 'config/initializers/rack_attack.rb'
- - 'config/initializers/twitter_regex.rb'
- - 'config/routes.rb'
- - 'lib/mastodon/premailer_webpack_strategy.rb'
- - 'lib/tasks/mastodon.rake'
-
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try!
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91a2c48a1c..425c098505 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,54 @@
All notable changes to this project will be documented in this file.
+## [4.1.3] - 2023-07-06
+
+### Added
+
+- Add fallback redirection when getting a webfinger query `LOCAL_DOMAIN@LOCAL_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23600))
+
+### Changed
+
+- Change OpenGraph-based embeds to allow fullscreen ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25058))
+- Change AccessTokensVacuum to also delete expired tokens ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24868))
+- Change profile updates to be sent to recently-mentioned servers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24852))
+- Change automatic post deletion thresholds and load detection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24614))
+- Change `/api/v1/statuses/:id/history` to always return at least one item ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25510))
+- Change auto-linking to allow carets in URL query params ([renchap](https://github.com/mastodon/mastodon/pull/25216))
+
+### Removed
+
+- Remove invalid `X-Frame-Options: ALLOWALL` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25070))
+
+### Fixed
+
+- Fix wrong view being displayed when a webhook fails validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25464))
+- Fix soft-deleted post cleanup scheduler overwhelming the streaming server ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25519))
+- Fix incorrect pagination headers in `/api/v2/admin/accounts` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25477))
+- Fix multiple inefficiencies in automatic post cleanup worker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24785), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24840))
+- Fix performance of streaming by parsing message JSON once ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25278), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25361))
+- Fix CSP headers when `S3_ALIAS_HOST` includes a path component ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25273))
+- Fix `tootctl accounts approve --number N` not aproving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605))
+- Fix reports not being closed when performing batch suspensions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24988))
+- Fix being able to vote on your own polls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25015))
+- Fix race condition when reblogging a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25016))
+- Fix “Authorized applications” inefficiently and incorrectly getting last use date ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25060))
+- Fix “Authorized applications” crashing when listing apps with certain admin API scopes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25713))
+- Fix multiple N+1s in ConversationsController ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25134), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25399), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25499))
+- Fix user archive takeouts when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24431))
+- Fix searching for remote content by URL not working under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25637))
+- Fix inefficiencies in indexing content for search ([VyrCossont](https://github.com/mastodon/mastodon/pull/24285), [VyrCossont](https://github.com/mastodon/mastodon/pull/24342))
+
+### Security
+
+- Add finer permission requirements for managing webhooks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25463))
+- Update dependencies
+- Add hardening headers for user-uploaded files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25756))
+- Fix verified links possibly hiding important parts of the URL (CVE-2023-36462)
+- Fix timeout handling of outbound HTTP requests (CVE-2023-36461)
+- Fix arbitrary file creation through media processing (CVE-2023-36460)
+- Fix possible XSS in preview cards (CVE-2023-36459)
+
## [4.1.2] - 2023-04-04
### Fixed
diff --git a/Gemfile b/Gemfile
index 88ac7c5ae4..7a0fbdc82d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,9 +3,7 @@
source 'https://rubygems.org'
ruby '>= 3.0.0'
-gem 'pkg-config', '~> 1.5'
-
-gem 'puma', '~> 6.2'
+gem 'puma', '~> 6.3'
gem 'rails', '~> 6.1.7'
gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 1.2'
@@ -17,10 +15,10 @@ gem 'makara', '~> 0.5'
gem 'pghero'
gem 'dotenv-rails', '~> 2.8'
-gem 'aws-sdk-s3', '~> 1.122', require: false
+gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 0.3', require: false
-gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b'
+gem 'kt-paperclip', '~> 7.2'
gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10'
@@ -59,8 +57,7 @@ gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
-gem 'nokogiri', '~> 1.14'
-gem 'nsa', '~> 0.2'
+gem 'nokogiri', '~> 1.15'
gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'
gem 'parslet'
diff --git a/Gemfile.lock b/Gemfile.lock
index bb1e91d61e..b2d75e9d4a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,18 +7,6 @@ GIT
hkdf (~> 0.2)
jwt (~> 2.0)
-GIT
- remote: https://github.com/kreeti/kt-paperclip.git
- revision: 11abf222dc31bff71160a1d138b445214f434b2b
- ref: 11abf222dc31bff71160a1d138b445214f434b2b
- specs:
- kt-paperclip (7.1.1)
- activemodel (>= 4.2.0)
- activesupport (>= 4.2.0)
- marcel (~> 1.0.1)
- mime-types
- terrapin (~> 0.6.0)
-
GIT
remote: https://github.com/mastodon/rails-settings-cached.git
revision: 86328ef0bd04ce21cc0504ff5e334591e8c2ccab
@@ -30,40 +18,40 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- actioncable (6.1.7.3)
- actionpack (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ actioncable (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.1.7.3)
- actionpack (= 6.1.7.3)
- activejob (= 6.1.7.3)
- activerecord (= 6.1.7.3)
- activestorage (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ actionmailbox (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ activejob (= 6.1.7.4)
+ activerecord (= 6.1.7.4)
+ activestorage (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
mail (>= 2.7.1)
- actionmailer (6.1.7.3)
- actionpack (= 6.1.7.3)
- actionview (= 6.1.7.3)
- activejob (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ actionmailer (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ actionview (= 6.1.7.4)
+ activejob (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (6.1.7.3)
- actionview (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ actionpack (6.1.7.4)
+ actionview (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (6.1.7.3)
- actionpack (= 6.1.7.3)
- activerecord (= 6.1.7.3)
- activestorage (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ actiontext (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ activerecord (= 6.1.7.4)
+ activestorage (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
nokogiri (>= 1.8.5)
- actionview (6.1.7.3)
- activesupport (= 6.1.7.3)
+ actionview (6.1.7.4)
+ activesupport (= 6.1.7.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@@ -73,22 +61,22 @@ GEM
activemodel (>= 4.1, < 7.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
- activejob (6.1.7.3)
- activesupport (= 6.1.7.3)
+ activejob (6.1.7.4)
+ activesupport (= 6.1.7.4)
globalid (>= 0.3.6)
- activemodel (6.1.7.3)
- activesupport (= 6.1.7.3)
- activerecord (6.1.7.3)
- activemodel (= 6.1.7.3)
- activesupport (= 6.1.7.3)
- activestorage (6.1.7.3)
- actionpack (= 6.1.7.3)
- activejob (= 6.1.7.3)
- activerecord (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ activemodel (6.1.7.4)
+ activesupport (= 6.1.7.4)
+ activerecord (6.1.7.4)
+ activemodel (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
+ activestorage (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ activejob (= 6.1.7.4)
+ activerecord (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (6.1.7.3)
+ activesupport (6.1.7.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -109,26 +97,26 @@ GEM
attr_required (1.0.1)
awrence (1.2.1)
aws-eventstream (1.2.0)
- aws-partitions (1.761.0)
- aws-sdk-core (3.172.0)
+ aws-partitions (1.780.0)
+ aws-sdk-core (3.175.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.64.0)
- aws-sdk-core (~> 3, >= 3.165.0)
+ aws-sdk-kms (1.67.0)
+ aws-sdk-core (~> 3, >= 3.174.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.122.0)
- aws-sdk-core (~> 3, >= 3.165.0)
+ aws-sdk-s3 (1.126.0)
+ aws-sdk-core (~> 3, >= 3.174.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.18)
- better_errors (2.9.1)
- coderay (>= 1.0.0)
+ better_errors (2.10.1)
erubi (>= 1.0.0)
rack (>= 0.9.0)
+ rouge (>= 1.0.0)
better_html (2.0.1)
actionview (>= 6.0)
activesupport (>= 6.0)
@@ -151,7 +139,7 @@ GEM
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
- capistrano (3.17.2)
+ capistrano (3.17.3)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
@@ -166,7 +154,7 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
- capybara (3.39.1)
+ capybara (3.39.2)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -186,7 +174,6 @@ GEM
chunky_png (1.4.0)
climate_control (0.2.0)
cocoon (1.2.15)
- coderay (1.1.3)
color_diff (0.1)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
@@ -241,7 +228,7 @@ GEM
erubi (1.12.0)
et-orbi (1.2.7)
tzinfo
- excon (0.99.0)
+ excon (0.100.0)
fabrication (2.30.0)
faker (3.2.0)
i18n (>= 1.8.11, < 2)
@@ -269,7 +256,7 @@ GEM
faraday-rack (1.0.0)
faraday-retry (1.0.3)
fast_blank (1.0.1)
- fastimage (2.2.6)
+ fastimage (2.2.7)
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
@@ -331,7 +318,7 @@ GEM
httplog (1.6.2)
rack (>= 2.0)
rainbow (>= 2.0.0)
- i18n (1.13.0)
+ i18n (1.14.1)
concurrent-ruby (~> 1.0)
i18n-tasks (1.0.12)
activesupport (>= 4.0.2)
@@ -367,7 +354,7 @@ GEM
json-schema (4.0.0)
addressable (>= 2.8)
jsonapi-renderer (0.2.2)
- jwt (2.7.0)
+ jwt (2.7.1)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
@@ -380,6 +367,12 @@ GEM
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
+ kt-paperclip (7.2.0)
+ activemodel (>= 4.2.0)
+ activesupport (>= 4.2.0)
+ marcel (~> 1.0.1)
+ mime-types
+ terrapin (~> 0.6.0)
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.8.1)
@@ -419,13 +412,13 @@ GEM
mime-types-data (3.2023.0218.1)
mini_mime (1.1.2)
mini_portile2 (2.8.2)
- minitest (5.18.0)
- msgpack (1.7.0)
+ minitest (5.18.1)
+ msgpack (1.7.1)
multi_json (1.15.0)
multipart-post (2.3.0)
net-http (0.3.2)
uri
- net-imap (0.3.4)
+ net-imap (0.3.6)
date
net-protocol
net-ldap (0.18.0)
@@ -439,15 +432,10 @@ GEM
net-protocol
net-ssh (7.1.0)
nio4r (2.5.9)
- nokogiri (1.14.3)
- mini_portile2 (~> 2.8.0)
+ nokogiri (1.15.2)
+ mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- nsa (0.2.8)
- activesupport (>= 4.2, < 7)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- sidekiq (>= 3.5)
- statsd-ruby (~> 1.4, >= 1.4.0)
- oj (3.14.3)
+ oj (3.15.0)
omniauth (1.9.2)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
@@ -481,15 +469,15 @@ GEM
orm_adapter (0.5.0)
ox (2.14.16)
parallel (1.23.0)
- parser (3.2.2.1)
+ parser (3.2.2.3)
ast (~> 2.4.1)
+ racc
parslet (2.0.0)
pastel (0.8.0)
tty-color (~> 0.5)
pg (1.5.3)
pghero (3.3.3)
activerecord (>= 6)
- pkg-config (1.5.1)
posix-spawn (0.3.15)
premailer (1.21.0)
addressable
@@ -501,12 +489,12 @@ GEM
premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0)
public_suffix (5.0.1)
- puma (6.2.2)
+ puma (6.3.0)
nio4r (~> 2.0)
pundit (2.3.0)
activesupport (>= 3.0.0)
raabro (1.4.0)
- racc (1.6.2)
+ racc (1.7.1)
rack (2.2.7)
rack-attack (6.6.1)
rack (>= 1.0, < 3)
@@ -522,20 +510,20 @@ GEM
rack
rack-test (2.1.0)
rack (>= 1.3)
- rails (6.1.7.3)
- actioncable (= 6.1.7.3)
- actionmailbox (= 6.1.7.3)
- actionmailer (= 6.1.7.3)
- actionpack (= 6.1.7.3)
- actiontext (= 6.1.7.3)
- actionview (= 6.1.7.3)
- activejob (= 6.1.7.3)
- activemodel (= 6.1.7.3)
- activerecord (= 6.1.7.3)
- activestorage (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ rails (6.1.7.4)
+ actioncable (= 6.1.7.4)
+ actionmailbox (= 6.1.7.4)
+ actionmailer (= 6.1.7.4)
+ actionpack (= 6.1.7.4)
+ actiontext (= 6.1.7.4)
+ actionview (= 6.1.7.4)
+ activejob (= 6.1.7.4)
+ activemodel (= 6.1.7.4)
+ activerecord (= 6.1.7.4)
+ activestorage (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
bundler (>= 1.15.0)
- railties (= 6.1.7.3)
+ railties (= 6.1.7.4)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
@@ -544,30 +532,31 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.5.0)
- loofah (~> 2.19, >= 2.19.1)
+ rails-html-sanitizer (1.6.0)
+ loofah (~> 2.21)
+ nokogiri (~> 1.14)
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
- railties (6.1.7.3)
- actionpack (= 6.1.7.3)
- activesupport (= 6.1.7.3)
+ railties (6.1.7.4)
+ actionpack (= 6.1.7.4)
+ activesupport (= 6.1.7.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.6)
- rdf (3.2.10)
+ rdf (3.2.11)
link_header (~> 0.0, >= 0.0.8)
- rdf-normalize (0.5.1)
+ rdf-normalize (0.6.0)
rdf (~> 3.2)
redcarpet (3.6.0)
redis (4.8.1)
- redis-namespace (1.10.0)
+ redis-namespace (1.11.0)
redis (>= 4)
redlock (1.3.2)
redis (>= 3.0.0, < 6.0)
- regexp_parser (2.8.0)
+ regexp_parser (2.8.1)
request_store (1.5.1)
rack (>= 1.4)
responders (3.1.0)
@@ -575,6 +564,7 @@ GEM
railties (>= 5.2)
rexml (3.2.5)
rotp (6.2.2)
+ rouge (4.1.2)
rpam2 (4.0.2)
rqrcode (2.2.0)
chunky_png (~> 1.0)
@@ -588,7 +578,7 @@ GEM
rspec-mocks (3.12.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
- rspec-rails (6.0.2)
+ rspec-rails (6.0.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@@ -601,20 +591,22 @@ GEM
sidekiq (>= 2.4.0)
rspec-support (3.12.0)
rspec_chunked (0.6)
- rubocop (1.51.0)
+ rubocop (1.52.1)
json (~> 2.3)
parallel (~> 1.10)
- parser (>= 3.2.0.0)
+ parser (>= 3.2.2.3)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.28.1)
+ rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.18.0)
rubocop (~> 1.41)
+ rubocop-factory_bot (2.23.1)
+ rubocop (~> 1.33)
rubocop-performance (1.18.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
@@ -622,16 +614,17 @@ GEM
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
- rubocop-rspec (2.19.0)
+ rubocop-rspec (2.22.0)
rubocop (~> 1.33)
rubocop-capybara (~> 2.17)
+ rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
ruby-saml (1.15.0)
nokogiri (>= 1.13.10)
rexml
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
- rufus-scheduler (3.8.2)
+ rufus-scheduler (3.9.1)
fugit (~> 1.1, >= 1.1.6)
safety_net_attestation (0.4.0)
jwt (~> 2.0)
@@ -642,13 +635,13 @@ GEM
activerecord (>= 4.0.0)
railties (>= 4.0.0)
semantic_range (3.0.0)
- sidekiq (6.5.8)
+ sidekiq (6.5.9)
connection_pool (>= 2.2.5, < 3)
rack (~> 2.0)
redis (>= 4.5.0, < 5)
sidekiq-bulk (0.2.0)
sidekiq
- sidekiq-scheduler (5.0.2)
+ sidekiq-scheduler (5.0.3)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
tilt (>= 1.4.0)
@@ -681,7 +674,6 @@ GEM
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
stackprof (0.2.25)
- statsd-ruby (1.5.0)
stoplight (3.0.1)
redlock (~> 1.0)
strong_migrations (0.8.0)
@@ -691,13 +683,13 @@ GEM
attr_required (>= 0.0.5)
httpclient (>= 2.4)
sysexits (1.2.0)
- temple (0.10.0)
+ temple (0.10.2)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
thor (1.2.2)
- tilt (2.1.0)
+ tilt (2.2.0)
timeout (0.3.2)
tpm-key_attestation (0.12.0)
bindata (~> 2.4)
@@ -724,7 +716,7 @@ GEM
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.4.2)
- uri (0.12.1)
+ uri (0.12.2)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
@@ -770,7 +762,7 @@ DEPENDENCIES
active_model_serializers (~> 0.10)
addressable (~> 2.8)
annotate (~> 3.2)
- aws-sdk-s3 (~> 1.122)
+ aws-sdk-s3 (~> 1.123)
better_errors (~> 2.9)
binding_of_caller (~> 1.0)
blurhash (~> 0.1)
@@ -818,7 +810,7 @@ DEPENDENCIES
json-ld-preloaded (~> 3.2)
json-schema (~> 4.0)
kaminari (~> 1.2)
- kt-paperclip (~> 7.1)!
+ kt-paperclip (~> 7.2)
letter_opener (~> 1.8)
letter_opener_web (~> 2.0)
link_header (~> 0.0)
@@ -829,8 +821,7 @@ DEPENDENCIES
mime-types (~> 3.4.1)
net-http (~> 0.3.2)
net-ldap (~> 0.18)
- nokogiri (~> 1.14)
- nsa (~> 0.2)
+ nokogiri (~> 1.15)
oj (~> 3.14)
omniauth (~> 1.9)
omniauth-cas (~> 2.0)
@@ -841,12 +832,11 @@ DEPENDENCIES
parslet
pg (~> 1.5)
pghero
- pkg-config (~> 1.5)
posix-spawn
premailer-rails
private_address_check (~> 0.5)
public_suffix (~> 5.0)
- puma (~> 6.2)
+ puma (~> 6.3)
pundit (~> 2.3)
rack (~> 2.2.7)
rack-attack (~> 6.6)
@@ -894,3 +884,9 @@ DEPENDENCIES
webpacker (~> 5.4)
webpush!
xorcist (~> 1.1)
+
+RUBY VERSION
+ ruby 3.2.2p53
+
+BUNDLED WITH
+ 2.4.13
diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb
index e38e14a106..abde8e92f1 100644
--- a/app/chewy/accounts_index.rb
+++ b/app/chewy/accounts_index.rb
@@ -2,8 +2,37 @@
class AccountsIndex < Chewy::Index
settings index: { refresh_interval: '30s' }, analysis: {
+ filter: {
+ english_stop: {
+ type: 'stop',
+ stopwords: '_english_',
+ },
+
+ english_stemmer: {
+ type: 'stemmer',
+ language: 'english',
+ },
+
+ english_possessive_stemmer: {
+ type: 'stemmer',
+ language: 'possessive_english',
+ },
+ },
+
analyzer: {
- content: {
+ natural: {
+ tokenizer: 'uax_url_email',
+ filter: %w(
+ english_possessive_stemmer
+ lowercase
+ asciifolding
+ cjk_width
+ english_stop
+ english_stemmer
+ ),
+ },
+
+ verbatim: {
tokenizer: 'whitespace',
filter: %w(lowercase asciifolding cjk_width),
},
@@ -26,18 +55,13 @@ class AccountsIndex < Chewy::Index
index_scope ::Account.searchable.includes(:account_stat)
root date_detection: false do
- field :id, type: 'long'
-
- field :display_name, type: 'text', analyzer: 'content' do
- field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
- end
-
- field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do
- field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
- end
-
- field :following_count, type: 'long', value: ->(account) { account.following_count }
- field :followers_count, type: 'long', value: ->(account) { account.followers_count }
- field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
+ field(:id, type: 'long')
+ field(:following_count, type: 'long')
+ field(:followers_count, type: 'long')
+ field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties })
+ 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' }
end
end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 099512248f..3a6df662ea 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -14,15 +14,5 @@ module Admin
@pending_tags_count = Tag.pending_review.count
@pending_appeals_count = Appeal.pending.count
end
-
- private
-
- def redis_info
- @redis_info ||= if redis.is_a?(Redis::Namespace)
- redis.redis.info
- else
- redis.info
- end
- end
end
end
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index 081550b762..b9691c5a3a 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -31,31 +31,41 @@ module Admin
@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
+ # Disallow accidentally downgrading a domain block
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
@domain_block.save
flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe
@domain_block.errors.delete(:domain)
- render :new
- else
- if existing_domain_block.present?
- @domain_block = existing_domain_block
- @domain_block.update(resource_params)
- end
+ return render :new
+ end
- if @domain_block.save
- DomainBlockWorker.perform_async(@domain_block.id)
- log_action :create, @domain_block
- redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
- else
- render :new
- end
+ # Allow transparently upgrading a domain block
+ if existing_domain_block.present?
+ @domain_block = existing_domain_block
+ @domain_block.assign_attributes(resource_params)
+ end
+
+ # Require explicit confirmation when suspending
+ return render :confirm_suspension if requires_confirmation?
+
+ if @domain_block.save
+ DomainBlockWorker.perform_async(@domain_block.id)
+ log_action :create, @domain_block
+ redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
+ else
+ render :new
end
end
def update
authorize :domain_block, :update?
- if @domain_block.update(update_params)
+ @domain_block.assign_attributes(update_params)
+
+ # Require explicit confirmation when suspending
+ return render :confirm_suspension if requires_confirmation?
+
+ if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id, @domain_block.severity_previously_changed?)
log_action :update, @domain_block
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
@@ -92,5 +102,9 @@ module Admin
def action_from_button
'save' if params[:save]
end
+
+ def requires_confirmation?
+ @domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.severity.to_s == 'suspend' && !params[:confirm]
+ end
end
end
diff --git a/app/controllers/admin/webhooks_controller.rb b/app/controllers/admin/webhooks_controller.rb
index 1ed3fd18ab..f1aad7c4b5 100644
--- a/app/controllers/admin/webhooks_controller.rb
+++ b/app/controllers/admin/webhooks_controller.rb
@@ -28,6 +28,7 @@ module Admin
authorize :webhook, :create?
@webhook = Webhook.new(resource_params)
+ @webhook.current_account = current_account
if @webhook.save
redirect_to admin_webhook_path(@webhook)
@@ -39,10 +40,12 @@ module Admin
def update
authorize @webhook, :update?
+ @webhook.current_account = current_account
+
if @webhook.update(resource_params)
redirect_to admin_webhook_path(@webhook)
else
- render :show
+ render :edit
end
end
@@ -71,7 +74,7 @@ module Admin
end
def resource_params
- params.require(:webhook).permit(:url, events: [])
+ params.require(:webhook).permit(:url, :template, events: [])
end
end
end
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 8af4242ba3..ddb94d5ca4 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -90,7 +90,7 @@ class Api::V1::AccountsController < Api::BaseController
end
def account_params
- params.permit(:username, :email, :password, :agreement, :locale, :reason)
+ params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone)
end
def check_enabled_registrations
diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb
index 9034e8a2f4..b3ca2f7903 100644
--- a/app/controllers/api/v1/conversations_controller.rb
+++ b/app/controllers/api/v1/conversations_controller.rb
@@ -11,7 +11,7 @@ class Api::V1::ConversationsController < Api::BaseController
def index
@conversations = paginated_conversations
- render json: @conversations, each_serializer: REST::ConversationSerializer
+ render json: @conversations, each_serializer: REST::ConversationSerializer, relationships: StatusRelationshipsPresenter.new(@conversations.map(&:last_status), current_user&.account_id)
end
def read
@@ -19,6 +19,11 @@ class Api::V1::ConversationsController < Api::BaseController
render json: @conversation, serializer: REST::ConversationSerializer
end
+ def unread
+ @conversation.update!(unread: true)
+ render json: @conversation, serializer: REST::ConversationSerializer
+ end
+
def destroy
@conversation.destroy!
render_empty
@@ -32,6 +37,19 @@ class Api::V1::ConversationsController < Api::BaseController
def paginated_conversations
AccountConversation.where(account: current_account)
+ .includes(
+ account: :account_stat,
+ last_status: [
+ :media_attachments,
+ :preview_cards,
+ :status_stat,
+ :tags,
+ {
+ active_mentions: [account: :account_stat],
+ account: :account_stat,
+ },
+ ]
+ )
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
diff --git a/app/controllers/api/v1/directories_controller.rb b/app/controllers/api/v1/directories_controller.rb
index c0585e8599..1109435507 100644
--- a/app/controllers/api/v1/directories_controller.rb
+++ b/app/controllers/api/v1/directories_controller.rb
@@ -21,11 +21,35 @@ class Api::V1::DirectoriesController < Api::BaseController
def accounts_scope
Account.discoverable.tap do |scope|
- scope.merge!(Account.local) if truthy_param?(:local)
- scope.merge!(Account.by_recent_status) if params[:order].blank? || params[:order] == 'active'
- scope.merge!(Account.order(id: :desc)) if params[:order] == 'new'
- scope.merge!(Account.not_excluded_by_account(current_account)) if current_account
- scope.merge!(Account.not_domain_blocked_by_account(current_account)) if current_account && !truthy_param?(:local)
+ scope.merge!(account_order_scope)
+ scope.merge!(local_account_scope) if local_accounts?
+ scope.merge!(account_exclusion_scope) if current_account
+ scope.merge!(account_domain_block_scope) if current_account && !local_accounts?
end
end
+
+ def local_accounts?
+ truthy_param?(:local)
+ end
+
+ def account_order_scope
+ case params[:order]
+ when 'new'
+ Account.order(id: :desc)
+ when 'active', nil
+ Account.by_recent_status
+ end
+ end
+
+ def local_account_scope
+ Account.local
+ end
+
+ def account_exclusion_scope
+ Account.not_excluded_by_account(current_account)
+ end
+
+ def account_domain_block_scope
+ Account.not_domain_blocked_by_account(current_account)
+ end
end
diff --git a/app/controllers/api/v1/emails/confirmations_controller.rb b/app/controllers/api/v1/emails/confirmations_controller.rb
index 29ff897b91..16e91b4497 100644
--- a/app/controllers/api/v1/emails/confirmations_controller.rb
+++ b/app/controllers/api/v1/emails/confirmations_controller.rb
@@ -5,6 +5,7 @@ class Api::V1::Emails::ConfirmationsController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :check
before_action :require_user_owned_by_application!, except: :check
before_action :require_user_not_confirmed!, except: :check
+ before_action :require_authenticated_user!, only: :check
def create
current_user.update!(email: params[:email]) if params.key?(:email)
diff --git a/app/controllers/api/v1/featured_tags_controller.rb b/app/controllers/api/v1/featured_tags_controller.rb
index 5c81877bd9..516046f009 100644
--- a/app/controllers/api/v1/featured_tags_controller.rb
+++ b/app/controllers/api/v1/featured_tags_controller.rb
@@ -31,8 +31,4 @@ class Api::V1::FeaturedTagsController < Api::BaseController
def set_featured_tags
@featured_tags = current_account.featured_tags.order(statuses_count: :desc)
end
-
- def featured_tag_params
- params.require(:name)
- end
end
diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb
index 843ca2ec2b..4bbbed2673 100644
--- a/app/controllers/api/v1/lists_controller.rb
+++ b/app/controllers/api/v1/lists_controller.rb
@@ -42,6 +42,6 @@ class Api::V1::ListsController < Api::BaseController
end
def list_params
- params.permit(:title, :replies_policy)
+ params.permit(:title, :replies_policy, :exclusive)
end
end
diff --git a/app/controllers/api/v1/statuses/histories_controller.rb b/app/controllers/api/v1/statuses/histories_controller.rb
index dff2425d06..2913472b04 100644
--- a/app/controllers/api/v1/statuses/histories_controller.rb
+++ b/app/controllers/api/v1/statuses/histories_controller.rb
@@ -8,11 +8,15 @@ class Api::V1::Statuses::HistoriesController < Api::BaseController
def show
cache_if_unauthenticated!
- render json: @status.edits.includes(:account, status: [:account]), each_serializer: REST::StatusEditSerializer
+ render json: status_edits, each_serializer: REST::StatusEditSerializer
end
private
+ def status_edits
+ @status.edits.includes(:account, status: [:account]).to_a.presence || [@status.build_snapshot(at_time: @status.edited_at || @status.created_at)]
+ end
+
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
diff --git a/app/controllers/api/v2/admin/accounts_controller.rb b/app/controllers/api/v2/admin/accounts_controller.rb
index 0c451f778c..65cf0c4db4 100644
--- a/app/controllers/api/v2/admin/accounts_controller.rb
+++ b/app/controllers/api/v2/admin/accounts_controller.rb
@@ -18,6 +18,14 @@ class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController
private
+ def next_path
+ api_v2_admin_accounts_url(pagination_params(max_id: pagination_max_id)) if records_continue?
+ end
+
+ def prev_path
+ api_v2_admin_accounts_url(pagination_params(min_id: pagination_since_id)) unless @accounts.empty?
+ end
+
def filtered_accounts
AccountFilter.new(translated_filter_params).results
end
diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb
index b084eae425..cc74db58e5 100644
--- a/app/controllers/api/v2/search_controller.rb
+++ b/app/controllers/api/v2/search_controller.rb
@@ -34,11 +34,11 @@ class Api::V2::SearchController < Api::BaseController
params[:q],
current_account,
limit_param(RESULTS_LIMIT),
- search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed))
+ search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed), following: truthy_param?(:following))
)
end
def search_params
- params.permit(:type, :offset, :min_id, :max_id, :account_id)
+ params.permit(:type, :offset, :min_id, :max_id, :account_id, :following)
end
end
diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb
index 1eb318bca2..3283c5f362 100644
--- a/app/controllers/auth/confirmations_controller.rb
+++ b/app/controllers/auth/confirmations_controller.rb
@@ -88,8 +88,10 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
def after_confirmation_path_for(_resource_name, user)
if user.created_by_application && truthy_param?(:redirect_to_app)
user.created_by_application.confirmation_redirect_uri
+ elsif user_signed_in?
+ web_url('start')
else
- super
+ new_user_session_path
end
end
end
diff --git a/app/controllers/backups_controller.rb b/app/controllers/backups_controller.rb
index 5891da6f6d..205df48d44 100644
--- a/app/controllers/backups_controller.rb
+++ b/app/controllers/backups_controller.rb
@@ -11,15 +11,15 @@ class BackupsController < ApplicationController
def download
case Paperclip::Attachment.default_options[:storage]
when :s3
- redirect_to @backup.dump.expiring_url(10)
+ redirect_to @backup.dump.expiring_url(10), allow_other_host: true
when :fog
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
- redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
+ redirect_to @backup.dump.expiring_url(Time.now.utc + 10), allow_other_host: true
else
- redirect_to full_asset_url(@backup.dump.url)
+ redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
end
when :filesystem
- redirect_to full_asset_url(@backup.dump.url)
+ redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
end
end
diff --git a/app/controllers/concerns/captcha_concern.rb b/app/controllers/concerns/captcha_concern.rb
index 538c1ffb14..576304d1ca 100644
--- a/app/controllers/concerns/captcha_concern.rb
+++ b/app/controllers/concerns/captcha_concern.rb
@@ -2,6 +2,7 @@
module CaptchaConcern
extend ActiveSupport::Concern
+
include Hcaptcha::Adapters::ViewMethods
included do
@@ -35,18 +36,22 @@ module CaptchaConcern
flash.delete(:hcaptcha_error)
yield message
end
+
false
end
end
def extend_csp_for_captcha!
policy = request.content_security_policy
+
return unless captcha_required? && policy.present?
%w(script_src frame_src style_src connect_src).each do |directive|
values = policy.send(directive)
+
values << 'https://hcaptcha.com' unless values.include?('https://hcaptcha.com') || values.include?('https:')
values << 'https://*.hcaptcha.com' unless values.include?('https://*.hcaptcha.com') || values.include?('https:')
+
policy.send(directive, *values)
end
end
diff --git a/app/controllers/concerns/theming_concern.rb b/app/controllers/concerns/theming_concern.rb
index f993a81d72..82a53dbf51 100644
--- a/app/controllers/concerns/theming_concern.rb
+++ b/app/controllers/concerns/theming_concern.rb
@@ -75,7 +75,7 @@ module ThemingConcern
end
fallbacks.each do |fallback|
- return resolve_pack(Themes.instance.flavour(fallback), pack_name) if Themes.instance.flavour(fallback)
+ return resolve_pack(Themes.instance.flavour(fallback), pack_name, skin) if Themes.instance.flavour(fallback)
end
nil
diff --git a/app/controllers/mail_subscriptions_controller.rb b/app/controllers/mail_subscriptions_controller.rb
new file mode 100644
index 0000000000..b071a80605
--- /dev/null
+++ b/app/controllers/mail_subscriptions_controller.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+class MailSubscriptionsController < ApplicationController
+ layout 'auth'
+
+ skip_before_action :require_functional!
+
+ before_action :set_body_classes
+ before_action :set_user
+ before_action :set_type
+
+ def show; end
+
+ def create
+ @user.settings[email_type_from_param] = false
+ @user.save!
+ end
+
+ private
+
+ def set_user
+ @user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
+ end
+
+ def set_body_classes
+ @body_classes = 'lighter'
+ end
+
+ def set_type
+ @type = email_type_from_param
+ end
+
+ def email_type_from_param
+ case params[:type]
+ when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
+ "notification_emails.#{params[:type]}"
+ else
+ raise ArgumentError
+ end
+ end
+end
diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb
index 41e20add62..ac820e92bc 100644
--- a/app/controllers/media_controller.rb
+++ b/app/controllers/media_controller.rb
@@ -46,7 +46,7 @@ class MediaController < ApplicationController
end
def allow_iframing
- response.headers['X-Frame-Options'] = 'ALLOWALL'
+ response.headers.delete('X-Frame-Options')
end
def set_pack
diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb
index bdbf8796fe..983caf22fa 100644
--- a/app/controllers/settings/imports_controller.rb
+++ b/app/controllers/settings/imports_controller.rb
@@ -12,6 +12,7 @@ class Settings::ImportsController < Settings::BaseController
muting: 'muted_accounts_failures.csv',
domain_blocking: 'blocked_domains_failures.csv',
bookmarks: 'bookmarks_failures.csv',
+ lists: 'lists_failures.csv',
}.freeze
TYPE_TO_HEADERS_MAP = {
@@ -20,6 +21,7 @@ class Settings::ImportsController < Settings::BaseController
muting: ['Account address', 'Hide notifications'],
domain_blocking: false,
bookmarks: false,
+ lists: false,
}.freeze
def index
@@ -49,6 +51,8 @@ class Settings::ImportsController < Settings::BaseController
csv << [row.data['domain']]
when :bookmarks
csv << [row.data['uri']]
+ when :lists
+ csv << [row.data['list_name'], row.data['acct']]
end
end
end
diff --git a/app/controllers/settings/preferences/base_controller.rb b/app/controllers/settings/preferences/base_controller.rb
index faf778a7e5..c1f8b49898 100644
--- a/app/controllers/settings/preferences/base_controller.rb
+++ b/app/controllers/settings/preferences/base_controller.rb
@@ -19,6 +19,6 @@ class Settings::Preferences::BaseController < Settings::BaseController
end
def user_params
- params.require(:user).permit(:locale, chosen_languages: [], settings_attributes: UserSettings.keys)
+ params.require(:user).permit(:locale, :time_zone, chosen_languages: [], settings_attributes: UserSettings.keys)
end
end
diff --git a/app/controllers/settings/verifications_controller.rb b/app/controllers/settings/verifications_controller.rb
new file mode 100644
index 0000000000..fc4f23bb18
--- /dev/null
+++ b/app/controllers/settings/verifications_controller.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class Settings::VerificationsController < Settings::BaseController
+ before_action :set_account
+
+ def show
+ @verified_links = @account.fields.select(&:verified?)
+ end
+
+ private
+
+ def set_account
+ @account = current_account
+ end
+end
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index 5758085988..0efafb8456 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -46,7 +46,7 @@ class StatusesController < ApplicationController
return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true
- response.headers['X-Frame-Options'] = 'ALLOWALL'
+ response.headers.delete('X-Frame-Options')
render layout: 'embedded'
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 3192c7ab56..3148756b75 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -52,7 +52,7 @@ module ApplicationHelper
if closed_registrations? || omniauth_only?
'https://joinmastodon.org/#getting-started'
else
- new_user_registration_path
+ ENV.fetch('SSO_ACCOUNT_SIGN_UP', new_user_registration_path)
end
end
@@ -170,11 +170,11 @@ module ApplicationHelper
end
def storage_host
- URI::HTTPS.build(host: storage_host_name).to_s
+ "https://#{storage_host_var}"
end
def storage_host?
- storage_host_name.present?
+ storage_host_var.present?
end
def quote_wrap(text, line_width: 80, break_sequence: "\n")
@@ -235,7 +235,7 @@ module ApplicationHelper
private
- def storage_host_name
+ def storage_host_var
ENV.fetch('S3_ALIAS_HOST', nil) || ENV.fetch('S3_CLOUDFRONT_HOST', nil)
end
end
diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb
index 5b2ac1a2ae..f44cf79730 100644
--- a/app/helpers/formatting_helper.rb
+++ b/app/helpers/formatting_helper.rb
@@ -54,6 +54,10 @@ module FormattingHelper
end
def account_field_value_format(field, with_rel_me: true)
- html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false)
+ if field.verified? && !field.account.local?
+ TextFormatter.shortened_link(field.value_for_verification)
+ else
+ html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false)
+ end
end
end
diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb
index 00e1e5178b..840a18d3e6 100644
--- a/app/helpers/languages_helper.rb
+++ b/app/helpers/languages_helper.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/ModuleLength
-
module LanguagesHelper
ISO_639_1 = {
aa: ['Afar', 'Afaraf'].freeze,
diff --git a/app/helpers/react_component_helper.rb b/app/helpers/react_component_helper.rb
index fc08de13dd..ce616e8306 100644
--- a/app/helpers/react_component_helper.rb
+++ b/app/helpers/react_component_helper.rb
@@ -11,7 +11,7 @@ module ReactComponentHelper
end
def react_admin_component(name, props = {})
- data = { 'admin-component': name.to_s.camelcase, props: Oj.dump({ locale: I18n.locale }.merge(props)) }
+ data = { 'admin-component': name.to_s.camelcase, props: Oj.dump(props) }
div_tag_with_data(data)
end
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 3d5592867c..889ca7f402 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -5,10 +5,6 @@ module SettingsHelper
LanguagesHelper::SUPPORTED_LOCALES.keys
end
- def hash_to_object(hash)
- HashObject.new(hash)
- end
-
def session_device_icon(session)
device = session.detection.device
@@ -28,13 +24,4 @@ module SettingsHelper
safe_join([image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), content_tag(:span, account.acct, class: 'username')], ' ')
end
end
-
- def picture_hint(hint, picture)
- if picture.original_filename.nil?
- hint
- else
- link = link_to t('generic.delete'), settings_profile_picture_path(picture.name.to_s), data: { method: :delete }
- safe_join([hint, link], '
'.html_safe)
- end
- end
end
diff --git a/app/javascript/core/admin.js b/app/javascript/core/admin.js
index ac1b2f95fb..97b2f4e309 100644
--- a/app/javascript/core/admin.js
+++ b/app/javascript/core/admin.js
@@ -2,6 +2,7 @@
import 'packs/public-path';
import { delegate } from '@rails/ujs';
+
import ready from '../mastodon/ready';
const setAnnouncementEndsAttributes = (target) => {
diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js
index c6cf6704c3..40537377c0 100644
--- a/app/javascript/core/settings.js
+++ b/app/javascript/core/settings.js
@@ -1,9 +1,9 @@
// This file will be loaded on settings pages, regardless of theme.
import 'packs/public-path';
+import { delegate } from '@rails/ujs';
import escapeTextContentForBrowser from 'escape-html';
-import { delegate } from '@rails/ujs';
import emojify from '../mastodon/features/emoji/emoji';
diff --git a/app/javascript/core/two_factor_authentication.js b/app/javascript/core/two_factor_authentication.js
index f076cdf30a..e76700a480 100644
--- a/app/javascript/core/two_factor_authentication.js
+++ b/app/javascript/core/two_factor_authentication.js
@@ -1,6 +1,8 @@
import 'packs/public-path';
-import axios from 'axios';
+
import * as WebAuthnJSON from '@github/webauthn-json';
+import axios from 'axios';
+
import ready from '../mastodon/ready';
import 'regenerator-runtime/runtime';
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js
index 9e5c85a15c..d4f18ff2d7 100644
--- a/app/javascript/flavours/glitch/actions/accounts.js
+++ b/app/javascript/flavours/glitch/actions/accounts.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import { importFetchedAccount, importFetchedAccounts } from './importer';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/announcements.js b/app/javascript/flavours/glitch/actions/announcements.js
index 586dcfd337..339c5f3adc 100644
--- a/app/javascript/flavours/glitch/actions/announcements.js
+++ b/app/javascript/flavours/glitch/actions/announcements.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { normalizeAnnouncement } from './importer/normalizer';
export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/blocks.js b/app/javascript/flavours/glitch/actions/blocks.js
index 192aa3ce40..e293657ad3 100644
--- a/app/javascript/flavours/glitch/actions/blocks.js
+++ b/app/javascript/flavours/glitch/actions/blocks.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
import { openModal } from './modal';
@@ -94,6 +95,6 @@ export function initBlockModal(account) {
account,
});
- dispatch(openModal('BLOCK'));
+ dispatch(openModal({ modalType: 'BLOCK' }));
};
}
diff --git a/app/javascript/flavours/glitch/actions/bookmarks.js b/app/javascript/flavours/glitch/actions/bookmarks.js
index 3c8eec5468..0b16f61e63 100644
--- a/app/javascript/flavours/glitch/actions/bookmarks.js
+++ b/app/javascript/flavours/glitch/actions/bookmarks.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import { importFetchedStatuses } from './importer';
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/boosts.js b/app/javascript/flavours/glitch/actions/boosts.js
index c0f0f3acc5..1fc2e391e2 100644
--- a/app/javascript/flavours/glitch/actions/boosts.js
+++ b/app/javascript/flavours/glitch/actions/boosts.js
@@ -14,7 +14,10 @@ export function initBoostModal(props) {
privacy,
});
- dispatch(openModal('BOOST', props));
+ dispatch(openModal({
+ modalType: 'BOOST',
+ modalProps: props,
+ }));
};
}
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index f0d50ccf13..9e0b123704 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -1,11 +1,14 @@
+import { defineMessages } from 'react-intl';
+
import axios from 'axios';
import { throttle } from 'lodash';
-import { defineMessages } from 'react-intl';
+
import api from 'flavours/glitch/api';
import { search as emojiSearch } from 'flavours/glitch/features/emoji/emoji_mart_search_light';
import { tagHistory } from 'flavours/glitch/settings';
import { recoverHashtags } from 'flavours/glitch/utils/hashtag';
import resizeImage from 'flavours/glitch/utils/resize_image';
+
import { showAlert, showAlertForError } from './alerts';
import { useEmoji } from './emojis';
import { importFetchedAccounts, importFetchedStatus } from './importer';
@@ -413,7 +416,10 @@ export function initMediaEditModal(id) {
id,
});
- dispatch(openModal('FOCAL_POINT', { id }));
+ dispatch(openModal({
+ modalType: 'FOCAL_POINT',
+ modalProps: { id },
+ }));
};
}
diff --git a/app/javascript/flavours/glitch/actions/conversations.js b/app/javascript/flavours/glitch/actions/conversations.js
index 4ef654b1f9..8c4c4529fb 100644
--- a/app/javascript/flavours/glitch/actions/conversations.js
+++ b/app/javascript/flavours/glitch/actions/conversations.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import {
importFetchedAccounts,
importFetchedStatuses,
diff --git a/app/javascript/flavours/glitch/actions/directory.js b/app/javascript/flavours/glitch/actions/directory.js
index 4b2b6dd56d..cda63f2b5a 100644
--- a/app/javascript/flavours/glitch/actions/directory.js
+++ b/app/javascript/flavours/glitch/actions/directory.js
@@ -1,6 +1,7 @@
import api from '../api';
-import { importFetchedAccounts } from './importer';
+
import { fetchRelationships } from './accounts';
+import { importFetchedAccounts } from './importer';
export const DIRECTORY_FETCH_REQUEST = 'DIRECTORY_FETCH_REQUEST';
export const DIRECTORY_FETCH_SUCCESS = 'DIRECTORY_FETCH_SUCCESS';
diff --git a/app/javascript/flavours/glitch/actions/favourites.js b/app/javascript/flavours/glitch/actions/favourites.js
index 7388e0c580..2d4d4e6206 100644
--- a/app/javascript/flavours/glitch/actions/favourites.js
+++ b/app/javascript/flavours/glitch/actions/favourites.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import { importFetchedStatuses } from './importer';
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/filters.js b/app/javascript/flavours/glitch/actions/filters.js
index e9c609fc87..a11956ac56 100644
--- a/app/javascript/flavours/glitch/actions/filters.js
+++ b/app/javascript/flavours/glitch/actions/filters.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { openModal } from './modal';
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';
@@ -14,9 +15,12 @@ export const FILTERS_CREATE_SUCCESS = 'FILTERS_CREATE_SUCCESS';
export const FILTERS_CREATE_FAIL = 'FILTERS_CREATE_FAIL';
export const initAddFilter = (status, { contextType }) => dispatch =>
- dispatch(openModal('FILTER', {
- statusId: status?.get('id'),
- contextType: contextType,
+ dispatch(openModal({
+ modalType: 'FILTER',
+ modalProps: {
+ statusId: status?.get('id'),
+ contextType: contextType,
+ },
}));
export const fetchFilters = () => (dispatch, getState) => {
diff --git a/app/javascript/flavours/glitch/actions/history.js b/app/javascript/flavours/glitch/actions/history.js
index c142aaf617..52401b7dce 100644
--- a/app/javascript/flavours/glitch/actions/history.js
+++ b/app/javascript/flavours/glitch/actions/history.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { importFetchedAccounts } from './importer';
export const HISTORY_FETCH_REQUEST = 'HISTORY_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js
index 1c9f524e43..540e6cba78 100644
--- a/app/javascript/flavours/glitch/actions/importer/normalizer.js
+++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js
@@ -1,11 +1,12 @@
import escapeTextContentForBrowser from 'escape-html';
+
import emojify from 'flavours/glitch/features/emoji/emoji';
-import { unescapeHTML } from 'flavours/glitch/utils/html';
import { autoHideCW } from 'flavours/glitch/utils/content_warning';
+import { unescapeHTML } from 'flavours/glitch/utils/html';
const domParser = new DOMParser();
-const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
+const makeEmojiMap = emojis => emojis.reduce((obj, emoji) => {
obj[`:${emoji.shortcode}:`] = emoji;
return obj;
}, {});
@@ -19,7 +20,7 @@ export function searchTextFromRawStatus (status) {
export function normalizeAccount(account) {
account = { ...account };
- const emojiMap = makeEmojiMap(account);
+ const emojiMap = makeEmojiMap(account.emojis);
const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name;
account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap);
@@ -77,7 +78,7 @@ export function normalizeStatus(status, normalOldStatus, settings) {
} else {
const spoilerText = normalStatus.spoiler_text || '';
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/
/g, '\n').replace(/<\/p>
/g, '\n\n');
- const emojiMap = makeEmojiMap(normalStatus);
+ const emojiMap = makeEmojiMap(normalStatus.emojis);
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
@@ -88,22 +89,48 @@ export function normalizeStatus(status, normalOldStatus, settings) {
return normalStatus;
}
+export function normalizeStatusTranslation(translation, status) {
+ const emojiMap = makeEmojiMap(status.get('emojis').toJS());
+
+ const normalTranslation = {
+ detected_source_language: translation.detected_source_language,
+ language: translation.language,
+ provider: translation.provider,
+ contentHtml: emojify(translation.content, emojiMap),
+ spoilerHtml: emojify(escapeTextContentForBrowser(translation.spoiler_text), emojiMap),
+ spoiler_text: translation.spoiler_text,
+ };
+
+ return normalTranslation;
+}
+
export function normalizePoll(poll) {
const normalPoll = { ...poll };
- const emojiMap = makeEmojiMap(normalPoll);
+ const emojiMap = makeEmojiMap(poll.emojis);
normalPoll.options = poll.options.map((option, index) => ({
...option,
voted: poll.own_votes && poll.own_votes.includes(index),
- title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
+ titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap),
}));
return normalPoll;
}
+export function normalizePollOptionTranslation(translation, poll) {
+ const emojiMap = makeEmojiMap(poll.get('emojis').toJS());
+
+ const normalTranslation = {
+ ...translation,
+ titleHtml: emojify(escapeTextContentForBrowser(translation.title), emojiMap),
+ };
+
+ return normalTranslation;
+}
+
export function normalizeAnnouncement(announcement) {
const normalAnnouncement = { ...announcement };
- const emojiMap = makeEmojiMap(normalAnnouncement);
+ const emojiMap = makeEmojiMap(normalAnnouncement.emojis);
normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap);
diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js
index fecc4d03b0..2a60941cfb 100644
--- a/app/javascript/flavours/glitch/actions/interactions.js
+++ b/app/javascript/flavours/glitch/actions/interactions.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { importFetchedAccounts, importFetchedStatus } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/lists.js b/app/javascript/flavours/glitch/actions/lists.js
index 5ab9224363..b0789cd426 100644
--- a/app/javascript/flavours/glitch/actions/lists.js
+++ b/app/javascript/flavours/glitch/actions/lists.js
@@ -1,6 +1,7 @@
import api from '../api';
-import { importFetchedAccounts } from './importer';
+
import { showAlertForError } from './alerts';
+import { importFetchedAccounts } from './importer';
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
@@ -150,10 +151,10 @@ export const createListFail = error => ({
error,
});
-export const updateList = (id, title, shouldReset, replies_policy) => (dispatch, getState) => {
+export const updateList = (id, title, shouldReset, isExclusive, replies_policy) => (dispatch, getState) => {
dispatch(updateListRequest(id));
- api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy }).then(({ data }) => {
+ api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy, exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive }).then(({ data }) => {
dispatch(updateListSuccess(data));
if (shouldReset) {
diff --git a/app/javascript/flavours/glitch/actions/local_settings.js b/app/javascript/flavours/glitch/actions/local_settings.js
index adf7fd2abc..f2878daa50 100644
--- a/app/javascript/flavours/glitch/actions/local_settings.js
+++ b/app/javascript/flavours/glitch/actions/local_settings.js
@@ -1,4 +1,5 @@
import { expandSpoilers, disableSwiping } from 'flavours/glitch/initial_state';
+
import { openModal } from './modal';
export const LOCAL_SETTING_CHANGE = 'LOCAL_SETTING_CHANGE';
@@ -27,9 +28,12 @@ export function checkDeprecatedLocalSettings() {
}
if (changed_settings.length > 0) {
- dispatch(openModal('DEPRECATED_SETTINGS', {
- settings: changed_settings,
- onConfirm: () => dispatch(clearDeprecatedLocalSettings()),
+ dispatch(openModal({
+ modalType: 'DEPRECATED_SETTINGS',
+ modalProps: {
+ settings: changed_settings,
+ onConfirm: () => dispatch(clearDeprecatedLocalSettings()),
+ },
}));
}
};
diff --git a/app/javascript/flavours/glitch/actions/markers.js b/app/javascript/flavours/glitch/actions/markers.js
index 8fd9da405e..ccb1b23d6f 100644
--- a/app/javascript/flavours/glitch/actions/markers.js
+++ b/app/javascript/flavours/glitch/actions/markers.js
@@ -1,8 +1,10 @@
-import api from '../api';
-import { debounce } from 'lodash';
-import { compareId } from '../compare_id';
import { List as ImmutableList } from 'immutable';
+import { debounce } from 'lodash';
+
+import api from '../api';
+import { compareId } from '../compare_id';
+
export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
export const MARKERS_FETCH_FAIL = 'MARKERS_FETCH_FAIL';
diff --git a/app/javascript/flavours/glitch/actions/modal.js b/app/javascript/flavours/glitch/actions/modal.js
deleted file mode 100644
index ef2ae0e4c7..0000000000
--- a/app/javascript/flavours/glitch/actions/modal.js
+++ /dev/null
@@ -1,18 +0,0 @@
-export const MODAL_OPEN = 'MODAL_OPEN';
-export const MODAL_CLOSE = 'MODAL_CLOSE';
-
-export function openModal(type, props) {
- return {
- type: MODAL_OPEN,
- modalType: type,
- modalProps: props,
- };
-}
-
-export function closeModal(type, options = { ignoreFocus: false }) {
- return {
- type: MODAL_CLOSE,
- modalType: type,
- ignoreFocus: options.ignoreFocus,
- };
-}
diff --git a/app/javascript/flavours/glitch/actions/modal.ts b/app/javascript/flavours/glitch/actions/modal.ts
new file mode 100644
index 0000000000..af34f5d6af
--- /dev/null
+++ b/app/javascript/flavours/glitch/actions/modal.ts
@@ -0,0 +1,17 @@
+import { createAction } from '@reduxjs/toolkit';
+
+import type { MODAL_COMPONENTS } from '../features/ui/components/modal_root';
+
+export type ModalType = keyof typeof MODAL_COMPONENTS;
+
+interface OpenModalPayload {
+ modalType: ModalType;
+ modalProps: unknown;
+}
+export const openModal = createAction('MODAL_OPEN');
+
+interface CloseModalPayload {
+ modalType: ModalType | undefined;
+ ignoreFocus: boolean;
+}
+export const closeModal = createAction('MODAL_CLOSE');
diff --git a/app/javascript/flavours/glitch/actions/mutes.js b/app/javascript/flavours/glitch/actions/mutes.js
index aa47d14642..4af927d932 100644
--- a/app/javascript/flavours/glitch/actions/mutes.js
+++ b/app/javascript/flavours/glitch/actions/mutes.js
@@ -1,7 +1,9 @@
+import { openModal } from 'flavours/glitch/actions/modal';
+
import api, { getLinks } from '../api';
+
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
-import { openModal } from 'flavours/glitch/actions/modal';
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
@@ -96,7 +98,7 @@ export function initMuteModal(account) {
account,
});
- dispatch(openModal('MUTE'));
+ dispatch(openModal({ modalType: 'MUTE' }));
};
}
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 0460d4ea28..f370905de0 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -1,5 +1,15 @@
+import { IntlMessageFormat } from 'intl-messageformat';
+import { defineMessages } from 'react-intl';
+
+import { List as ImmutableList } from 'immutable';
+
+import { compareId } from 'flavours/glitch/compare_id';
+import { usePendingItems as preferPendingItems } from 'flavours/glitch/initial_state';
+import { unescapeHTML } from 'flavours/glitch/utils/html';
+import { requestNotificationPermission } from 'flavours/glitch/utils/notifications';
+
import api, { getLinks } from '../api';
-import IntlMessageFormat from 'intl-messageformat';
+
import { fetchFollowRequests, fetchRelationships } from './accounts';
import {
importFetchedAccount,
@@ -9,12 +19,9 @@ import {
} from './importer';
import { submitMarkers } from './markers';
import { saveSettings } from './settings';
-import { defineMessages } from 'react-intl';
-import { List as ImmutableList } from 'immutable';
-import { unescapeHTML } from 'flavours/glitch/utils/html';
-import { usePendingItems as preferPendingItems } from 'flavours/glitch/initial_state';
-import { compareId } from 'flavours/glitch/compare_id';
-import { requestNotificationPermission } from 'flavours/glitch/utils/notifications';
+
+
+
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
diff --git a/app/javascript/flavours/glitch/actions/onboarding.js b/app/javascript/flavours/glitch/actions/onboarding.js
index 5038b7eb67..a4a525c427 100644
--- a/app/javascript/flavours/glitch/actions/onboarding.js
+++ b/app/javascript/flavours/glitch/actions/onboarding.js
@@ -6,7 +6,9 @@ export function showOnboardingOnce() {
const alreadySeen = getState().getIn(['settings', 'onboarded']);
if (!alreadySeen) {
- dispatch(openModal('ONBOARDING'));
+ dispatch(openModal({
+ modalType: 'ONBOARDING',
+ }));
dispatch(changeSetting(['onboarded'], true));
dispatch(saveSettings());
}
diff --git a/app/javascript/flavours/glitch/actions/pin_statuses.js b/app/javascript/flavours/glitch/actions/pin_statuses.js
index a0d5933b1c..8aca199e97 100644
--- a/app/javascript/flavours/glitch/actions/pin_statuses.js
+++ b/app/javascript/flavours/glitch/actions/pin_statuses.js
@@ -1,7 +1,9 @@
+import { me } from 'flavours/glitch/initial_state';
+
import api from '../api';
+
import { importFetchedStatuses } from './importer';
-import { me } from 'flavours/glitch/initial_state';
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
diff --git a/app/javascript/flavours/glitch/actions/polls.js b/app/javascript/flavours/glitch/actions/polls.js
index 8e8b82df5d..a37410dc90 100644
--- a/app/javascript/flavours/glitch/actions/polls.js
+++ b/app/javascript/flavours/glitch/actions/polls.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { importFetchedPoll } from './importer';
export const POLL_VOTE_REQUEST = 'POLL_VOTE_REQUEST';
diff --git a/app/javascript/flavours/glitch/actions/push_notifications/index.js b/app/javascript/flavours/glitch/actions/push_notifications/index.js
index 9dcc4bd4bb..46b63867f1 100644
--- a/app/javascript/flavours/glitch/actions/push_notifications/index.js
+++ b/app/javascript/flavours/glitch/actions/push_notifications/index.js
@@ -1,5 +1,5 @@
-import { setAlerts } from './setter';
import { saveSettings } from './registerer';
+import { setAlerts } from './setter';
export function changeAlerts(path, value) {
return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
index bc5634233f..336bbc6869 100644
--- a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
+++ b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
@@ -1,5 +1,6 @@
import api from '../../api';
import { pushNotificationsSetting } from '../../settings';
+
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
// Taken from https://www.npmjs.com/package/web-push
diff --git a/app/javascript/flavours/glitch/actions/reports.js b/app/javascript/flavours/glitch/actions/reports.js
index fbe5b3791b..756b8cd05e 100644
--- a/app/javascript/flavours/glitch/actions/reports.js
+++ b/app/javascript/flavours/glitch/actions/reports.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { openModal } from './modal';
export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
@@ -6,9 +7,12 @@ export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
export const REPORT_SUBMIT_FAIL = 'REPORT_SUBMIT_FAIL';
export const initReport = (account, status) => dispatch =>
- dispatch(openModal('REPORT', {
- accountId: account.get('id'),
- statusId: status?.get('id'),
+ dispatch(openModal({
+ modalType: 'REPORT',
+ modalProps: {
+ accountId: account.get('id'),
+ statusId: status?.get('id'),
+ },
}));
export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => {
diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js
index 0012808e5b..d5154c6a84 100644
--- a/app/javascript/flavours/glitch/actions/search.js
+++ b/app/javascript/flavours/glitch/actions/search.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer';
diff --git a/app/javascript/flavours/glitch/actions/server.js b/app/javascript/flavours/glitch/actions/server.js
index 091af0f0fe..65f3efc3a7 100644
--- a/app/javascript/flavours/glitch/actions/server.js
+++ b/app/javascript/flavours/glitch/actions/server.js
@@ -1,4 +1,5 @@
import api from '../api';
+
import { importFetchedAccount } from './importer';
export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';
@@ -18,6 +19,10 @@ export const SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS = 'SERVER_DOMAIN_BLOCKS_FETCH_SU
export const SERVER_DOMAIN_BLOCKS_FETCH_FAIL = 'SERVER_DOMAIN_BLOCKS_FETCH_FAIL';
export const fetchServer = () => (dispatch, getState) => {
+ if (getState().getIn(['server', 'server', 'isLoading'])) {
+ return;
+ }
+
dispatch(fetchServerRequest());
api(getState)
@@ -65,6 +70,10 @@ const fetchServerTranslationLanguagesFail = error => ({
});
export const fetchExtendedDescription = () => (dispatch, getState) => {
+ if (getState().getIn(['server', 'extendedDescription', 'isLoading'])) {
+ return;
+ }
+
dispatch(fetchExtendedDescriptionRequest());
api(getState)
@@ -88,6 +97,10 @@ const fetchExtendedDescriptionFail = error => ({
});
export const fetchDomainBlocks = () => (dispatch, getState) => {
+ if (getState().getIn(['server', 'domainBlocks', 'isLoading'])) {
+ return;
+ }
+
dispatch(fetchDomainBlocksRequest());
api(getState)
diff --git a/app/javascript/flavours/glitch/actions/settings.js b/app/javascript/flavours/glitch/actions/settings.js
index 60f0abf950..120ae133ed 100644
--- a/app/javascript/flavours/glitch/actions/settings.js
+++ b/app/javascript/flavours/glitch/actions/settings.js
@@ -1,5 +1,7 @@
-import api from '../api';
import { debounce } from 'lodash';
+
+import api from '../api';
+
import { showAlertForError } from './alerts';
export const SETTING_CHANGE = 'SETTING_CHANGE';
diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js
index 487cd69884..5bdd31c343 100644
--- a/app/javascript/flavours/glitch/actions/statuses.js
+++ b/app/javascript/flavours/glitch/actions/statuses.js
@@ -1,8 +1,8 @@
import api from '../api';
-import { deleteFromTimelines } from './timelines';
-import { importFetchedStatus, importFetchedStatuses } from './importer';
import { ensureComposeIsVisible, setComposeToStatus } from './compose';
+import { importFetchedStatus, importFetchedStatuses } from './importer';
+import { deleteFromTimelines } from './timelines';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
@@ -344,7 +344,8 @@ export const translateStatusFail = (id, error) => ({
error,
});
-export const undoStatusTranslation = id => ({
+export const undoStatusTranslation = (id, pollId) => ({
type: STATUS_TRANSLATE_UNDO,
id,
+ pollId,
});
diff --git a/app/javascript/flavours/glitch/actions/store.js b/app/javascript/flavours/glitch/actions/store.js
index 137b68e222..e57b37a122 100644
--- a/app/javascript/flavours/glitch/actions/store.js
+++ b/app/javascript/flavours/glitch/actions/store.js
@@ -1,4 +1,5 @@
import { Iterable, fromJS } from 'immutable';
+
import { hydrateCompose } from './compose';
import { importFetchedAccounts } from './importer';
import { saveSettings } from './settings';
diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js
index b4eb60ad4e..f1c44d2e29 100644
--- a/app/javascript/flavours/glitch/actions/streaming.js
+++ b/app/javascript/flavours/glitch/actions/streaming.js
@@ -1,6 +1,18 @@
// @ts-check
+import { getLocale } from 'flavours/glitch/locales';
+
import { connectStream } from '../stream';
+
+import {
+ fetchAnnouncements,
+ updateAnnouncements,
+ updateReaction as updateAnnouncementsReaction,
+ deleteAnnouncement,
+} from './announcements';
+import { updateConversations } from './conversations';
+import { updateNotifications, expandNotifications } from './notifications';
+import { updateStatus } from './statuses';
import {
updateTimeline,
deleteFromTimelines,
@@ -12,18 +24,6 @@ import {
fillCommunityTimelineGaps,
fillListTimelineGaps,
} from './timelines';
-import { updateNotifications, expandNotifications } from './notifications';
-import { updateConversations } from './conversations';
-import { updateStatus } from './statuses';
-import {
- fetchAnnouncements,
- updateAnnouncements,
- updateReaction as updateAnnouncementsReaction,
- deleteAnnouncement,
-} from './announcements';
-import { getLocale } from 'mastodon/locales';
-
-const { messages } = getLocale();
/**
* @param {number} max
@@ -42,8 +42,10 @@ const randomUpTo = max =>
* @param {function(object): boolean} [options.accept]
* @returns {function(): void}
*/
-export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) =>
- connectStream(channelName, params, (dispatch, getState) => {
+export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => {
+ const { messages } = getLocale();
+
+ return connectStream(channelName, params, (dispatch, getState) => {
const locale = getState().getIn(['meta', 'locale']);
// @ts-expect-error
@@ -120,6 +122,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
},
};
});
+};
/**
* @param {Function} dispatch
diff --git a/app/javascript/flavours/glitch/actions/suggestions.js b/app/javascript/flavours/glitch/actions/suggestions.js
index 9e8cd1ea40..870a311024 100644
--- a/app/javascript/flavours/glitch/actions/suggestions.js
+++ b/app/javascript/flavours/glitch/actions/suggestions.js
@@ -1,6 +1,7 @@
import api from '../api';
-import { importFetchedAccounts } from './importer';
+
import { fetchRelationships } from './accounts';
+import { importFetchedAccounts } from './importer';
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST';
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS';
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index 603759760b..7d4d56a784 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -1,11 +1,13 @@
-import { importFetchedStatus, importFetchedStatuses } from './importer';
-import { submitMarkers } from './markers';
-import api, { getLinks } from 'flavours/glitch/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+
+import api, { getLinks } from 'flavours/glitch/api';
import { compareId } from 'flavours/glitch/compare_id';
import { usePendingItems as preferPendingItems } from 'flavours/glitch/initial_state';
import { toServerSideType } from 'flavours/glitch/utils/filters';
+import { importFetchedStatus, importFetchedStatuses } from './importer';
+import { submitMarkers } from './markers';
+
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
diff --git a/app/javascript/flavours/glitch/actions/trends.js b/app/javascript/flavours/glitch/actions/trends.js
index edda0b5b5d..d314423884 100644
--- a/app/javascript/flavours/glitch/actions/trends.js
+++ b/app/javascript/flavours/glitch/actions/trends.js
@@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
+
import { importFetchedStatuses } from './importer';
export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';
diff --git a/app/javascript/flavours/glitch/api.js b/app/javascript/flavours/glitch/api.js
index 42b64d6cc5..948ffbc95c 100644
--- a/app/javascript/flavours/glitch/api.js
+++ b/app/javascript/flavours/glitch/api.js
@@ -2,8 +2,8 @@
import axios from 'axios';
import LinkHeader from 'http-link-header';
-import ready from './ready';
+import ready from './ready';
/**
* @param {import('axios').AxiosResponse} response
* @returns {LinkHeader}
diff --git a/app/javascript/flavours/glitch/components/account.jsx b/app/javascript/flavours/glitch/components/account.jsx
index 35f83beab1..518464b040 100644
--- a/app/javascript/flavours/glitch/components/account.jsx
+++ b/app/javascript/flavours/glitch/components/account.jsx
@@ -1,20 +1,24 @@
-import React, { Fragment } from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
+
+import { defineMessages, injectIntl } from 'react-intl';
+
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+import { Skeleton } from 'flavours/glitch/components/skeleton';
+import { me } from 'flavours/glitch/initial_state';
+
import { Avatar } from './avatar';
import { DisplayName } from './display_name';
-import Permalink from './permalink';
import { IconButton } from './icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'flavours/glitch/initial_state';
+import Permalink from './permalink';
import { RelativeTimestamp } from './relative_timestamp';
-import { Skeleton } from 'flavours/glitch/components/skeleton';
+
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
- requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
+ requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
@@ -97,10 +101,10 @@ class Account extends ImmutablePureComponent {
if (hidden) {
return (
-
+ <>
{account.get('display_name')}
{account.get('username')}
-
+ >
);
}
@@ -128,10 +132,10 @@ class Account extends ImmutablePureComponent {
hidingNotificationsButton = ;
}
buttons = (
-
+ <>
{hidingNotificationsButton}
-
+ >
);
} else if (defaultAction === 'mute') {
buttons = ;
diff --git a/app/javascript/flavours/glitch/components/admin/Counter.jsx b/app/javascript/flavours/glitch/components/admin/Counter.jsx
index 9c0b848d0b..9bb792fc9d 100644
--- a/app/javascript/flavours/glitch/components/admin/Counter.jsx
+++ b/app/javascript/flavours/glitch/components/admin/Counter.jsx
@@ -1,9 +1,13 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import api from 'flavours/glitch/api';
+import { PureComponent } from 'react';
+
import { FormattedNumber } from 'react-intl';
-import { Sparklines, SparklinesCurve } from 'react-sparklines';
+
import classNames from 'classnames';
+
+import { Sparklines, SparklinesCurve } from 'react-sparklines';
+
+import api from 'flavours/glitch/api';
import { Skeleton } from 'flavours/glitch/components/skeleton';
const percIncrease = (a, b) => {
@@ -24,7 +28,7 @@ const percIncrease = (a, b) => {
return percent;
};
-export default class Counter extends React.PureComponent {
+export default class Counter extends PureComponent {
static propTypes = {
measure: PropTypes.string.isRequired,
@@ -62,25 +66,25 @@ export default class Counter extends React.PureComponent {
if (loading) {
content = (
-
+ <>
-
+ >
);
} else {
const measure = data[0];
const percentChange = measure.previous_total && percIncrease(measure.previous_total * 1, measure.total * 1);
content = (
-
+ <>
{measure.human_value || }
{measure.previous_total && ( 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'})}
-
+ >
);
}
const inner = (
-
+ <>
{content}
@@ -96,7 +100,7 @@ export default class Counter extends React.PureComponent {
)}
-
+ >
);
if (href) {
diff --git a/app/javascript/flavours/glitch/components/admin/Dimension.jsx b/app/javascript/flavours/glitch/components/admin/Dimension.jsx
index 8b8466ee78..793fe2dd76 100644
--- a/app/javascript/flavours/glitch/components/admin/Dimension.jsx
+++ b/app/javascript/flavours/glitch/components/admin/Dimension.jsx
@@ -1,11 +1,13 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import api from 'flavours/glitch/api';
-import { FormattedNumber } from 'react-intl';
-import { roundTo10 } from 'flavours/glitch/utils/numbers';
-import { Skeleton } from 'flavours/glitch/components/skeleton';
+import { PureComponent } from 'react';
-export default class Dimension extends React.PureComponent {
+import { FormattedNumber } from 'react-intl';
+
+import api from 'flavours/glitch/api';
+import { Skeleton } from 'flavours/glitch/components/skeleton';
+import { roundTo10 } from 'flavours/glitch/utils/numbers';
+
+export default class Dimension extends PureComponent {
static propTypes = {
dimension: PropTypes.string.isRequired,
diff --git a/app/javascript/flavours/glitch/components/admin/ImpactReport.jsx b/app/javascript/flavours/glitch/components/admin/ImpactReport.jsx
new file mode 100644
index 0000000000..9ec1460fcf
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/admin/ImpactReport.jsx
@@ -0,0 +1,91 @@
+import PropTypes from 'prop-types';
+import { PureComponent } from 'react';
+
+import { FormattedNumber, FormattedMessage } from 'react-intl';
+
+import classNames from 'classnames';
+
+import api from 'flavours/glitch/api';
+import { Skeleton } from 'flavours/glitch/components/skeleton';
+
+export default class ImpactReport extends PureComponent {
+
+ static propTypes = {
+ domain: PropTypes.string.isRequired,
+ };
+
+ state = {
+ loading: true,
+ data: null,
+ };
+
+ componentDidMount () {
+ const { domain } = this.props;
+
+ const params = {
+ domain: domain,
+ include_subdomains: true,
+ };
+
+ api().post('/api/v1/admin/measures', {
+ keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
+ start_at: null,
+ end_at: null,
+ instance_accounts: params,
+ instance_follows: params,
+ instance_followers: params,
+ }).then(res => {
+ this.setState({
+ loading: false,
+ data: res.data,
+ });
+ }).catch(err => {
+ console.error(err);
+ });
+ }
+
+ render () {
+ const { loading, data } = this.state;
+
+ return (
+
+
+
+
+
+
+
+
+ |
+
+
+ {loading ? : }
+ |
+
+
+ 0 })}>
+
+
+ |
+
+
+ {loading ? : }
+ |
+
+
+ 0 })}>
+
+
+ |
+
+
+ {loading ? : }
+ |
+
+
+
+
+ );
+ }
+
+}
diff --git a/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx b/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx
index ecefe7a840..d72465e4ad 100644
--- a/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx
+++ b/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx
@@ -1,16 +1,19 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import api from 'flavours/glitch/api';
+import { PureComponent } from 'react';
+
import { injectIntl, defineMessages } from 'react-intl';
+
import classNames from 'classnames';
+import api from 'flavours/glitch/api';
+
const messages = defineMessages({
other: { id: 'report.categories.other', defaultMessage: 'Other' },
spam: { id: 'report.categories.spam', defaultMessage: 'Spam' },
violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
});
-class Category extends React.PureComponent {
+class Category extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
@@ -52,7 +55,7 @@ class Category extends React.PureComponent {
}
-class Rule extends React.PureComponent {
+class Rule extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
@@ -84,7 +87,7 @@ class Rule extends React.PureComponent {
}
-class ReportReasonSelector extends React.PureComponent {
+class ReportReasonSelector extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
diff --git a/app/javascript/flavours/glitch/components/admin/Retention.jsx b/app/javascript/flavours/glitch/components/admin/Retention.jsx
index e1ba3f6c9d..2cfc30b6fb 100644
--- a/app/javascript/flavours/glitch/components/admin/Retention.jsx
+++ b/app/javascript/flavours/glitch/components/admin/Retention.jsx
@@ -1,8 +1,11 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import api from 'flavours/glitch/api';
+import { PureComponent } from 'react';
+
import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
+
import classNames from 'classnames';
+
+import api from 'flavours/glitch/api';
import { roundTo10 } from 'flavours/glitch/utils/numbers';
const dateForCohort = cohort => {
@@ -14,7 +17,7 @@ const dateForCohort = cohort => {
}
};
-export default class Retention extends React.PureComponent {
+export default class Retention extends PureComponent {
static propTypes = {
start_at: PropTypes.string,
diff --git a/app/javascript/flavours/glitch/components/admin/Trends.jsx b/app/javascript/flavours/glitch/components/admin/Trends.jsx
index 774bf36e6e..975ea6e0f2 100644
--- a/app/javascript/flavours/glitch/components/admin/Trends.jsx
+++ b/app/javascript/flavours/glitch/components/admin/Trends.jsx
@@ -1,11 +1,14 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import api from 'flavours/glitch/api';
+import { PureComponent } from 'react';
+
import { FormattedMessage } from 'react-intl';
+
import classNames from 'classnames';
+
+import api from 'flavours/glitch/api';
import Hashtag from 'flavours/glitch/components/hashtag';
-export default class Trends extends React.PureComponent {
+export default class Trends extends PureComponent {
static propTypes = {
limit: PropTypes.number.isRequired,
diff --git a/app/javascript/flavours/glitch/components/animated_number.tsx b/app/javascript/flavours/glitch/components/animated_number.tsx
index b6b073161b..81e0af395b 100644
--- a/app/javascript/flavours/glitch/components/animated_number.tsx
+++ b/app/javascript/flavours/glitch/components/animated_number.tsx
@@ -1,4 +1,5 @@
-import React, { useCallback, useState } from 'react';
+import { useCallback, useState } from 'react';
+import * as React from 'react';
import { TransitionMotion, spring } from 'react-motion';
diff --git a/app/javascript/flavours/glitch/components/attachment_list.jsx b/app/javascript/flavours/glitch/components/attachment_list.jsx
index 92ba98126a..173157b0d5 100644
--- a/app/javascript/flavours/glitch/components/attachment_list.jsx
+++ b/app/javascript/flavours/glitch/components/attachment_list.jsx
@@ -1,9 +1,12 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
+
import { FormattedMessage } from 'react-intl';
+
import classNames from 'classnames';
+
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
import { Icon } from 'flavours/glitch/components/icon';
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
diff --git a/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx b/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx
index 83fafbd10d..32a996fd7c 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx
+++ b/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx
@@ -1,10 +1,10 @@
-import React from 'react';
import PropTypes from 'prop-types';
-import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
+import { PureComponent } from 'react';
+import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
import { assetHost } from 'flavours/glitch/utils/config';
-export default class AutosuggestEmoji extends React.PureComponent {
+export default class AutosuggestEmoji extends PureComponent {
static propTypes = {
emoji: PropTypes.object.isRequired,
diff --git a/app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx b/app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx
deleted file mode 100644
index d787ed07ad..0000000000
--- a/app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ShortNumber from 'flavours/glitch/components/short_number';
-import { FormattedMessage } from 'react-intl';
-
-export default class AutosuggestHashtag extends React.PureComponent {
-
- static propTypes = {
- tag: PropTypes.shape({
- name: PropTypes.string.isRequired,
- url: PropTypes.string,
- history: PropTypes.array,
- }).isRequired,
- };
-
- render() {
- const { tag } = this.props;
- const weeklyUses = tag.history && (
- total + day.uses * 1, 0)}
- />
- );
-
- return (
-
-
- #{tag.name}
-
- {tag.history !== undefined && (
-
-
-
- )}
-
- );
- }
-
-}
diff --git a/app/javascript/flavours/glitch/components/autosuggest_hashtag.tsx b/app/javascript/flavours/glitch/components/autosuggest_hashtag.tsx
new file mode 100644
index 0000000000..932370884a
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/autosuggest_hashtag.tsx
@@ -0,0 +1,42 @@
+import { FormattedMessage } from 'react-intl';
+
+import ShortNumber from 'flavours/glitch/components/short_number';
+
+interface Props {
+ tag: {
+ name: string;
+ url?: string;
+ history?: Array<{
+ uses: number;
+ accounts: string;
+ day: string;
+ }>;
+ following?: boolean;
+ type: 'hashtag';
+ };
+}
+
+export const AutosuggestHashtag: React.FC = ({ tag }) => {
+ const weeklyUses = tag.history && (
+ total + day.uses * 1, 0)}
+ />
+ );
+
+ return (
+
+
+ #{tag.name}
+
+ {tag.history !== undefined && (
+
+
+
+ )}
+
+ );
+};
diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.jsx b/app/javascript/flavours/glitch/components/autosuggest_input.jsx
index 9e74590e60..f0833c8c6b 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_input.jsx
+++ b/app/javascript/flavours/glitch/components/autosuggest_input.jsx
@@ -1,12 +1,15 @@
-import React from 'react';
-import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
-import AutosuggestEmoji from './autosuggest_emoji';
-import AutosuggestHashtag from './autosuggest_hashtag';
-import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
+
import classNames from 'classnames';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
+
+import AutosuggestEmoji from './autosuggest_emoji';
+import { AutosuggestHashtag } from './autosuggest_hashtag';
+
const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => {
let word;
diff --git a/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx b/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx
index fc763146eb..25ca3fefa5 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx
+++ b/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx
@@ -1,13 +1,17 @@
-import React from 'react';
-import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
-import AutosuggestEmoji from './autosuggest_emoji';
-import AutosuggestHashtag from './autosuggest_hashtag';
-import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Textarea from 'react-textarea-autosize';
+
import classNames from 'classnames';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+import Textarea from 'react-textarea-autosize';
+
+import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
+
+import AutosuggestEmoji from './autosuggest_emoji';
+import { AutosuggestHashtag } from './autosuggest_hashtag';
+
const textAtCursorMatchesToken = (str, caretPosition) => {
let word;
diff --git a/app/javascript/flavours/glitch/components/avatar_composite.jsx b/app/javascript/flavours/glitch/components/avatar_composite.jsx
index 1c23a8b36e..5503abf4a9 100644
--- a/app/javascript/flavours/glitch/components/avatar_composite.jsx
+++ b/app/javascript/flavours/glitch/components/avatar_composite.jsx
@@ -1,9 +1,11 @@
-import React from 'react';
import PropTypes from 'prop-types';
+import { PureComponent } from 'react';
+
import ImmutablePropTypes from 'react-immutable-proptypes';
+
import { autoPlayGif } from 'flavours/glitch/initial_state';
-export default class AvatarComposite extends React.PureComponent {
+export default class AvatarComposite extends PureComponent {
static propTypes = {
accounts: ImmutablePropTypes.list.isRequired,
diff --git a/app/javascript/flavours/glitch/components/avatar_overlay.jsx b/app/javascript/flavours/glitch/components/avatar_overlay.jsx
index 01dec587a5..d8215a4785 100644
--- a/app/javascript/flavours/glitch/components/avatar_overlay.jsx
+++ b/app/javascript/flavours/glitch/components/avatar_overlay.jsx
@@ -1,9 +1,11 @@
-import React from 'react';
import PropTypes from 'prop-types';
+import { PureComponent } from 'react';
+
import ImmutablePropTypes from 'react-immutable-proptypes';
+
import { autoPlayGif } from 'flavours/glitch/initial_state';
-export default class AvatarOverlay extends React.PureComponent {
+export default class AvatarOverlay extends PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/flavours/glitch/components/blurhash.tsx b/app/javascript/flavours/glitch/components/blurhash.tsx
index 1550d0b7a5..d98e7d35db 100644
--- a/app/javascript/flavours/glitch/components/blurhash.tsx
+++ b/app/javascript/flavours/glitch/components/blurhash.tsx
@@ -1,4 +1,5 @@
-import React, { useRef, useEffect } from 'react';
+import { useRef, useEffect } from 'react';
+import * as React from 'react';
import { decode } from 'blurhash';
diff --git a/app/javascript/flavours/glitch/components/button.jsx b/app/javascript/flavours/glitch/components/button.jsx
index 40b8f5a159..bdeeeac999 100644
--- a/app/javascript/flavours/glitch/components/button.jsx
+++ b/app/javascript/flavours/glitch/components/button.jsx
@@ -1,8 +1,9 @@
-import React from 'react';
import PropTypes from 'prop-types';
+import { PureComponent } from 'react';
+
import classNames from 'classnames';
-export default class Button extends React.PureComponent {
+export default class Button extends PureComponent {
static propTypes = {
text: PropTypes.node,
diff --git a/app/javascript/flavours/glitch/components/check.jsx b/app/javascript/flavours/glitch/components/check.jsx
index ee2ef1595a..d818480b7b 100644
--- a/app/javascript/flavours/glitch/components/check.jsx
+++ b/app/javascript/flavours/glitch/components/check.jsx
@@ -1,5 +1,3 @@
-import React from 'react';
-
const Check = () => (