diff --git a/.eslintrc.js b/.eslintrc.js
index 8bc6e0895..206faa1c7 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',
@@ -293,6 +302,7 @@ module.exports = {
'.*rc.js',
'ide-helper.js',
'config/webpack/**/*',
+ 'config/formatjs-formatter.js',
],
env: {
diff --git a/.github/renovate.json5 b/.github/renovate.json5
new file mode 100644
index 000000000..1ae40d416
--- /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 e282e2ab7..b67c503e9 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 e13d227bd..4d3c2ce5a 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 2ddbca781..56d817123 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 7700e4851..1f0cfd1e7 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 98f101ad9..8712d8bd8 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 6f76dd60c..d19a0470d 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 de54fe9ae..0395c8639 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 6f79babcf..295e9610b 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 6a8035210..131a62a57 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 1c4958550..3306105f9 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 212b2cfe7..a91fd819a 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 310153929..50266fb8a 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 f284745ea..07cb1d41f 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 12ca46342..d1ed30b26 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 af0411e9c..27b6d5458 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 bd561df1d..eff89bdae 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
@@ -205,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 2cb7bd0e5..4964cf856 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -239,79 +239,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:
@@ -468,30 +395,6 @@ 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
@@ -1336,11 +1239,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:
@@ -1474,7 +1372,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'
@@ -1488,52 +1385,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/Gemfile b/Gemfile
index 51308c24d..84f210f48 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,7 +5,7 @@ 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 +17,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'
@@ -60,7 +60,6 @@ gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.15'
-gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'
gem 'parslet'
diff --git a/Gemfile.lock b/Gemfile.lock
index 36f7d7201..c4fa1822e 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
@@ -109,17 +97,17 @@ 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.772.0)
+ aws-sdk-core (3.174.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.65.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.123.0)
+ aws-sdk-core (~> 3, >= 3.174.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
@@ -380,6 +368,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)
@@ -442,11 +436,6 @@ GEM
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)
omniauth (1.9.2)
hashie (>= 3.4.6)
@@ -501,7 +490,7 @@ 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)
@@ -544,8 +533,9 @@ 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)
@@ -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)
@@ -648,7 +638,7 @@ GEM
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 +671,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)
@@ -770,7 +759,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 +807,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)
@@ -830,7 +819,6 @@ DEPENDENCIES
net-http (~> 0.3.2)
net-ldap (~> 0.18)
nokogiri (~> 1.15)
- nsa (~> 0.2)
oj (~> 3.14)
omniauth (~> 1.9)
omniauth-cas (~> 2.0)
@@ -846,7 +834,7 @@ DEPENDENCIES
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 +882,9 @@ DEPENDENCIES
webpacker (~> 5.4)
webpush!
xorcist (~> 1.1)
+
+RUBY VERSION
+ ruby 3.2.2p53
+
+BUNDLED WITH
+ 2.4.13
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 099512248..3a6df662e 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 081550b76..b9691c5a3 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 1ed3fd18a..01d9ba8ce 100644
--- a/app/controllers/admin/webhooks_controller.rb
+++ b/app/controllers/admin/webhooks_controller.rb
@@ -71,7 +71,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 8af4242ba..ddb94d5ca 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 9034e8a2f..c55500f76 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
@@ -32,7 +32,20 @@ class Api::V1::ConversationsController < Api::BaseController
def paginated_conversations
AccountConversation.where(account: current_account)
- .to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+ .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
def insert_pagination_headers
diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb
index 843ca2ec2..4bbbed267 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/backups_controller.rb b/app/controllers/backups_controller.rb
index 5891da6f6..205df48d4 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/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb
index bdbf8796f..983caf22f 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 faf778a7e..c1f8b4989 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/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 014e3a3f9..3148756b7 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -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/react_component_helper.rb b/app/helpers/react_component_helper.rb
index fc08de13d..ce616e830 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 3d5592867..ae89cec78 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
diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js
index f0d47f95e..540e6cba7 100644
--- a/app/javascript/flavours/glitch/actions/importer/normalizer.js
+++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js
@@ -6,7 +6,7 @@ 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;
}, {});
@@ -20,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);
@@ -78,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); @@ -89,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/lists.js b/app/javascript/flavours/glitch/actions/lists.js index 2faa54b95..b0789cd42 100644 --- a/app/javascript/flavours/glitch/actions/lists.js +++ b/app/javascript/flavours/glitch/actions/lists.js @@ -151,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/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index 47d45d001..a80746b75 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -1,4 +1,4 @@ -import IntlMessageFormat from 'intl-messageformat'; +import { IntlMessageFormat } from 'intl-messageformat'; import { defineMessages } from 'react-intl'; import { List as ImmutableList } from 'immutable'; diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js index 9bc9bf387..5bdd31c34 100644 --- a/app/javascript/flavours/glitch/actions/statuses.js +++ b/app/javascript/flavours/glitch/actions/statuses.js @@ -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/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js index e16a7409e..f1c44d2e2 100644 --- a/app/javascript/flavours/glitch/actions/streaming.js +++ b/app/javascript/flavours/glitch/actions/streaming.js @@ -1,6 +1,6 @@ // @ts-check -import { getLocale } from 'mastodon/locales'; +import { getLocale } from 'flavours/glitch/locales'; import { connectStream } from '../stream'; @@ -25,8 +25,6 @@ import { fillListTimelineGaps, } from './timelines'; -const { messages } = getLocale(); - /** * @param {number} max * @returns {number} @@ -44,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 @@ -122,6 +122,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti }, }; }); +}; /** * @param {Function} dispatch 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 000000000..9ec1460fc --- /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 ? |
+
+ |
+
+
+ {loading ? |
+
+ |
+
+
+ {loading ? |
+