diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 0369521963f16c..137bebc599d9bb 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -70,7 +70,7 @@ services: hard: -1 libretranslate: - image: libretranslate/libretranslate:v1.3.12 + image: libretranslate/libretranslate:v1.4.1 restart: unless-stopped volumes: - lt-data:/home/libretranslate/.local diff --git a/.env.test b/.env.test index 761d0d9210690d..2f8c1afd6e26e7 100644 --- a/.env.test +++ b/.env.test @@ -1,5 +1,5 @@ -# Node.js -NODE_ENV=tests +# In test, compile the NodeJS code as if we are in production +NODE_ENV=production # Federation LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_HTTPS=true diff --git a/.github/actions/setup-javascript/action.yml b/.github/actions/setup-javascript/action.yml new file mode 100644 index 00000000000000..c0f2957fb18037 --- /dev/null +++ b/.github/actions/setup-javascript/action.yml @@ -0,0 +1,19 @@ +name: 'Setup Javascript' +description: 'Setup a Javascript environment ready to run the Mastodon code' +inputs: + onlyProduction: + description: Only install production dependencies + default: 'false' + +runs: + using: 'composite' + steps: + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + cache: yarn + node-version-file: '.nvmrc' + + - name: Install all yarn packages + shell: bash + run: yarn --frozen-lockfile ${{ inputs.onlyProduction != 'false' && '--production' || '' }} diff --git a/.github/actions/setup-ruby/action.yml b/.github/actions/setup-ruby/action.yml new file mode 100644 index 00000000000000..3a6fba940201a7 --- /dev/null +++ b/.github/actions/setup-ruby/action.yml @@ -0,0 +1,23 @@ +name: 'Setup RUby' +description: 'Setup a Ruby environment ready to run the Mastodon code' +inputs: + ruby-version: + description: The Ruby version to install + default: '.ruby-version' + additional-system-dependencies: + description: 'Additional packages to install' + +runs: + using: 'composite' + steps: + - name: Install system dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y libicu-dev libidn11-dev ${{ inputs.additional-system-dependencies }} + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ inputs.ruby-version }} + bundler-cache: true diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml index bfb93a36cd052c..bbc31598c75a91 100644 --- a/.github/workflows/bundler-audit.yml +++ b/.github/workflows/bundler-audit.yml @@ -27,14 +27,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Run bundler-audit run: bundle exec bundler-audit diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index 39cf32ddc43a66..ceb385933b29bb 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -19,25 +19,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby + + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Check for missing strings in English JSON run: | diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index 8268788be42b1a..d3988d2f1a3ae1 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -44,14 +44,8 @@ jobs: run: sudo chown -R runner:docker . # This is needed to run the normalize step - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Run i18n normalize task run: bundle exec i18n-tasks normalize diff --git a/.github/workflows/lint-css.yml b/.github/workflows/lint-css.yml index bd775dba2061b5..7229bec582210b 100644 --- a/.github/workflows/lint-css.yml +++ b/.github/workflows/lint-css.yml @@ -35,14 +35,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - uses: xt0rted/stylelint-problem-matcher@v1 diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index ca9bd66a4ae1b1..8dcab845ee085c 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -30,16 +30,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Install native Ruby dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Run haml-lint run: | diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml index 67d28589cb09ca..1c1ecc2b22049b 100644 --- a/.github/workflows/lint-js.yml +++ b/.github/workflows/lint-js.yml @@ -39,14 +39,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: ESLint run: yarn lint:js --max-warnings 0 diff --git a/.github/workflows/lint-json.yml b/.github/workflows/lint-json.yml index 1d98c52673f7f3..7796bf92c4aeb1 100644 --- a/.github/workflows/lint-json.yml +++ b/.github/workflows/lint-json.yml @@ -31,14 +31,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Prettier run: yarn lint:json diff --git a/.github/workflows/lint-md.yml b/.github/workflows/lint-md.yml index 1b3f92c972730c..51c59937a30c44 100644 --- a/.github/workflows/lint-md.yml +++ b/.github/workflows/lint-md.yml @@ -31,14 +31,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Prettier run: yarn lint:md diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml index 92882a084d126e..411b323486a0a4 100644 --- a/.github/workflows/lint-ruby.yml +++ b/.github/workflows/lint-ruby.yml @@ -31,14 +31,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Set-up RuboCop Problem Matcher uses: r7kamura/rubocop-problem-matchers-action@v1 diff --git a/.github/workflows/lint-yml.yml b/.github/workflows/lint-yml.yml index e77cc988919279..908bdef5ccfa6c 100644 --- a/.github/workflows/lint-yml.yml +++ b/.github/workflows/lint-yml.yml @@ -33,14 +33,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Prettier run: yarn lint:yml diff --git a/.github/workflows/test-js.yml b/.github/workflows/test-js.yml index 0ef1d9b7c82797..79622b6c1f605d 100644 --- a/.github/workflows/test-js.yml +++ b/.github/workflows/test-js.yml @@ -35,14 +35,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install all yarn packages - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Jest testing 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 59287e88cf2553..5dca8e376da09f 100644 --- a/.github/workflows/test-migrations-one-step.yml +++ b/.github/workflows/test-migrations-one-step.yml @@ -72,16 +72,8 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install native Ruby dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Create database run: './bin/rails db:create' diff --git a/.github/workflows/test-migrations-two-step.yml b/.github/workflows/test-migrations-two-step.yml index 8f3c84d8f36884..59485d285df50d 100644 --- a/.github/workflows/test-migrations-two-step.yml +++ b/.github/workflows/test-migrations-two-step.yml @@ -71,16 +71,8 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install native Ruby dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev - - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - name: Create database run: './bin/rails db:create' diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index f2d2d02fc0d875..07fd25fb1bffe5 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -34,36 +34,29 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install native Ruby dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript with: - ruby-version: .ruby-version - bundler-cache: true + onlyProduction: 'true' - - run: yarn --frozen-lockfile --production - name: Precompile assets # Previously had set this, but it's not supported # export NODE_OPTIONS=--openssl-legacy-provider run: |- ./bin/rails assets:precompile + - name: Archive asset artifacts + run: | + tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* + - uses: actions/upload-artifact@v3 if: matrix.mode == 'test' with: path: |- - ./public/assets - ./public/packs-test + ./artifacts.tar.gz name: ${{ github.sha }} retention-days: 0 @@ -112,7 +105,6 @@ jobs: SAML_ENABLED: true CAS_ENABLED: true BUNDLE_WITH: 'pam_authentication test' - CI_JOBS: ${{ matrix.ci_job }}/4 GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }} strategy: @@ -122,38 +114,28 @@ jobs: - '3.0' - '3.1' - '.ruby-version' - ci_job: - - 1 - - 2 - - 3 - - 4 steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v3 with: - path: './public' + path: './' name: ${{ github.sha }} - - name: Update package index - run: sudo apt-get update - - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Install additional system dependencies - run: sudo apt-get install -y ffmpeg imagemagick libpam-dev + - name: Expand archived asset artifacts + run: | + tar xvzf artifacts.tar.gz - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby with: ruby-version: ${{ matrix.ruby-version}} - bundler-cache: true + additional-system-dependencies: ffmpeg imagemagick libpam-dev - name: Load database schema run: './bin/rails db:create db:schema:load db:seed' - - run: bundle exec rake rspec_chunked + - run: bin/rspec test-e2e: name: End to End testing @@ -210,28 +192,14 @@ jobs: path: './public' name: ${{ github.sha }} - - name: Update package index - run: sudo apt-get update - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Install additional system dependencies - run: sudo apt-get install -y ffmpeg imagemagick - - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby with: ruby-version: ${{ matrix.ruby-version}} - bundler-cache: true + additional-system-dependencies: ffmpeg imagemagick - - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Load database schema run: './bin/rails db:create db:schema:load db:seed' @@ -328,28 +296,14 @@ jobs: path: './public' name: ${{ github.sha }} - - name: Update package index - run: sudo apt-get update - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - cache: yarn - node-version-file: '.nvmrc' - - - name: Install native Ruby dependencies - run: sudo apt-get install -y libicu-dev libidn11-dev - - - name: Install additional system dependencies - run: sudo apt-get install -y ffmpeg imagemagick - - - name: Set up bundler cache - uses: ruby/setup-ruby@v1 + - name: Set up Ruby environment + uses: ./.github/actions/setup-ruby with: ruby-version: ${{ matrix.ruby-version}} - bundler-cache: true + additional-system-dependencies: ffmpeg imagemagick - - run: yarn --frozen-lockfile + - name: Set up Javascript environment + uses: ./.github/actions/setup-javascript - name: Load database schema run: './bin/rails db:create db:schema:load db:seed' diff --git a/.haml-lint.yml b/.haml-lint.yml index d1ed30b260c06a..8cfcaec8d93fec 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -12,3 +12,5 @@ linters: enabled: true MiddleDot: enabled: true + LineLength: + max: 320 diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 8a4dbaeff49334..9686c177bd23dd 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -1,33 +1,34 @@ # This configuration was generated by # `haml-lint --auto-gen-config` -# on 2023-10-11 11:31:24 -0400 using Haml-Lint version 0.51.0. +# on 2023-10-26 09:32:34 -0400 using Haml-Lint version 0.51.0. # The point is for the user to remove these configuration records # one by one as the lints are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of Haml-Lint, may require this file to be generated again. linters: - # Offense count: 946 + # Offense count: 16 LineLength: - enabled: false - - # Offense count: 22 - UnnecessaryStringOutput: - enabled: false - - # Offense count: 44 - RuboCop: - enabled: false - - # Offense count: 3 - ViewLength: exclude: - - 'app/views/admin/accounts/show.html.haml' - - 'app/views/admin/reports/show.html.haml' - - 'app/views/disputes/strikes/show.html.haml' + - 'app/views/admin/account_actions/new.html.haml' + - 'app/views/admin/accounts/index.html.haml' + - 'app/views/admin/ip_blocks/new.html.haml' + - 'app/views/admin/roles/_form.html.haml' + - 'app/views/admin/settings/discovery/show.html.haml' + - 'app/views/auth/registrations/edit.html.haml' + - 'app/views/auth/registrations/new.html.haml' + - 'app/views/filters/_filter_fields.html.haml' + - 'app/views/media/player.html.haml' + - 'app/views/settings/applications/_fields.html.haml' + - 'app/views/settings/imports/index.html.haml' + - 'app/views/settings/preferences/appearance/show.html.haml' + - 'app/views/settings/preferences/notifications/show.html.haml' + - 'app/views/settings/preferences/other/show.html.haml' - # Offense count: 2 - IdNames: + # Offense count: 9 + RuboCop: exclude: - - 'app/views/oauth/authorizations/error.html.haml' - - 'app/views/shared/_error_messages.html.haml' + - 'app/views/admin/accounts/_buttons.html.haml' + - 'app/views/admin/accounts/_local_account.html.haml' + - 'app/views/admin/roles/_form.html.haml' + - 'app/views/layouts/application.html.haml' diff --git a/.nvmrc b/.nvmrc index fa69d015bdb41e..48ef2c10babeda 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.8 +20.9 diff --git a/.rubocop.yml b/.rubocop.yml index 64ec766b223732..63de5e17c785f9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -27,7 +27,7 @@ AllCops: - 'node_modules/**/*' - 'Vagrantfile' - 'vendor/**/*' - - 'lib/json_ld/*' # Generated files + - 'config/initializers/json_ld*' # Generated files - 'lib/mastodon/migration_helpers.rb' # Vendored from GitLab - 'lib/templates/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9e04be03c26cfa..6409181601173c 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.56.1. +# using RuboCop version 1.57.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 @@ -130,11 +130,6 @@ RSpec/InstanceVariable: RSpec/LetSetup: Exclude: - - 'spec/controllers/admin/accounts_controller_spec.rb' - - 'spec/controllers/admin/action_logs_controller_spec.rb' - - 'spec/controllers/admin/instances_controller_spec.rb' - - 'spec/controllers/admin/reports/actions_controller_spec.rb' - - 'spec/controllers/admin/statuses_controller_spec.rb' - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb' - 'spec/controllers/api/v1/filters_controller_spec.rb' - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb' @@ -215,19 +210,6 @@ Rails/ApplicationController: Exclude: - 'app/controllers/health_controller.rb' -# Configuration parameters: Include. -# Include: db/**/*.rb -Rails/CreateTableWithTimestamps: - Exclude: - - 'db/migrate/20170508230434_create_conversation_mutes.rb' - - 'db/migrate/20170823162448_create_status_pins.rb' - - 'db/migrate/20171116161857_create_list_accounts.rb' - - 'db/migrate/20180929222014_create_account_conversations.rb' - - 'db/migrate/20181007025445_create_pghero_space_stats.rb' - - 'db/migrate/20190103124649_create_scheduled_statuses.rb' - - 'db/migrate/20220824233535_create_status_trends.rb' - - 'db/migrate/20221006061337_create_preview_card_trends.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity. Rails/DuplicateAssociation: @@ -269,7 +251,6 @@ Rails/LexicallyScopedActionFilter: Exclude: - 'app/controllers/auth/passwords_controller.rb' - 'app/controllers/auth/registrations_controller.rb' - - 'app/controllers/auth/sessions_controller.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Rails/NegateInclude: @@ -285,7 +266,6 @@ Rails/NegateInclude: - 'app/models/custom_filter.rb' - 'app/services/activitypub/process_status_update_service.rb' - 'app/services/fetch_link_card_service.rb' - - 'app/services/search_service.rb' - 'app/workers/web/push_notification_worker.rb' - 'lib/paperclip/color_extractor.rb' @@ -305,24 +285,6 @@ Rails/RakeEnvironment: - 'lib/tasks/repo.rake' - 'lib/tasks/statistics.rake' -# Configuration parameters: Include. -# Include: db/**/*.rb -Rails/ReversibleMigration: - Exclude: - - 'db/migrate/20160223164502_make_uris_nullable_in_statuses.rb' - - 'db/migrate/20161122163057_remove_unneeded_indexes.rb' - - 'db/migrate/20170205175257_remove_devices.rb' - - 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb' - - 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb' - - 'db/migrate/20170609145826_remove_default_language_from_statuses.rb' - - 'db/migrate/20170711225116_fix_null_booleans.rb' - - 'db/migrate/20171129172043_add_index_on_stream_entries.rb' - - 'db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb' - - 'db/migrate/20171226094803_more_faster_index_on_notifications.rb' - - 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb' - - 'db/migrate/20180617162849_remove_unused_indexes.rb' - - 'db/migrate/20220827195229_change_canonical_email_blocks_nullable.rb' - # Configuration parameters: ForbiddenMethods, AllowedMethods. # ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all Rails/SkipsModelValidations: @@ -375,30 +337,6 @@ Rails/SkipsModelValidations: - 'spec/services/follow_service_spec.rb' - 'spec/services/update_account_service_spec.rb' -# Configuration parameters: Include. -# Include: db/**/*.rb -Rails/ThreeStateBooleanColumn: - Exclude: - - 'db/migrate/20160325130944_add_admin_to_users.rb' - - 'db/migrate/20161123093447_add_sensitive_to_statuses.rb' - - 'db/migrate/20170123203248_add_reject_media_to_domain_blocks.rb' - - 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb' - - 'db/migrate/20170209184350_add_reply_to_statuses.rb' - - 'db/migrate/20170330163835_create_imports.rb' - - 'db/migrate/20170905165803_add_local_to_statuses.rb' - - 'db/migrate/20181203021853_add_discoverable_to_accounts.rb' - - 'db/migrate/20190509164208_add_by_moderator_to_tombstone.rb' - - 'db/migrate/20190805123746_add_capabilities_to_tags.rb' - - 'db/migrate/20191212163405_add_hide_collections_to_accounts.rb' - - 'db/migrate/20200309150742_add_forwarded_to_reports.rb' - - 'db/migrate/20210609202149_create_login_activities.rb' - - 'db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb' - - 'db/migrate/20211031031021_create_preview_card_providers.rb' - - 'db/migrate/20211115032527_add_trendable_to_preview_cards.rb' - - 'db/migrate/20220202200743_add_trendable_to_accounts.rb' - - 'db/migrate/20220202200926_add_trendable_to_statuses.rb' - - 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb' - # Configuration parameters: Include. # Include: app/models/**/*.rb Rails/UniqueValidationWithoutIndex: @@ -462,7 +400,7 @@ Style/CaseEquality: Exclude: - 'config/initializers/trusted_proxies.rb' -# This cop supports safe autocorrection (--autocorrect). +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, AllowedPatterns. # AllowedMethods: ==, equal?, eql? Style/ClassEqualityComparison: @@ -670,7 +608,6 @@ Style/RedundantReturn: Style/SafeNavigation: Exclude: - 'app/models/concerns/account_finder_concern.rb' - - 'app/models/status.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. diff --git a/Dockerfile b/Dockerfile index 8e40091add4c9e..600336de92aef2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN apt-get update && \ libgdbm-dev \ libgmp-dev \ libssl-dev \ - libyaml-0-2 \ + libyaml-dev \ ca-certificates \ libreadline8 \ python3 \ diff --git a/Gemfile b/Gemfile index 98897536a3d06e..04874debd541b0 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' ruby '>= 3.0.0' gem 'puma', '~> 6.3' -gem 'rails', '~> 7.0' +gem 'rails', '~> 7.1.1' gem 'sprockets', '~> 3.7.2' gem 'thor', '~> 1.2' gem 'rack', '~> 2.2.7' @@ -23,7 +23,7 @@ gem 'blurhash', '~> 0.1' gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.8' -gem 'bootsnap', '~> 1.16.0', require: false +gem 'bootsnap', '~> 1.17.0', require: false gem 'browser' gem 'charlock_holmes', '~> 0.7.7' gem 'chewy', '~> 7.3' @@ -88,7 +88,7 @@ gem 'simple-navigation', '~> 4.4' gem 'simple_form', '~> 5.2' gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie' gem 'stoplight', '~> 3.0.1' -gem 'strong_migrations', '~> 0.8' +gem 'strong_migrations', '1.3.0' gem 'tty-prompt', '~> 0.23', require: false gem 'twitter-text', '~> 3.1.0' gem 'tzinfo-data', '~> 1.2023' @@ -103,9 +103,6 @@ gem 'rdf-normalize', '~> 0.5' gem 'private_address_check', '~> 0.5' group :test do - # Used to split testing into chunks in CI - gem 'rspec_chunked', '~> 0.6' - # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab gem 'rspec-github', '~> 2.4', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 29cdc8aa4d3caa..4ebc600829244e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,75 +39,83 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8) - actionpack (= 7.0.8) - activesupport (= 7.0.8) + actioncable (7.1.1) + actionpack (= 7.1.1) + activesupport (= 7.1.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8) - actionpack (= 7.0.8) - activejob (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + zeitwerk (~> 2.6) + actionmailbox (7.1.1) + actionpack (= 7.1.1) + activejob (= 7.1.1) + activerecord (= 7.1.1) + activestorage (= 7.1.1) + activesupport (= 7.1.1) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8) - actionpack (= 7.0.8) - actionview (= 7.0.8) - activejob (= 7.0.8) - activesupport (= 7.0.8) + actionmailer (7.1.1) + actionpack (= 7.1.1) + actionview (= 7.1.1) + activejob (= 7.1.1) + activesupport (= 7.1.1) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.8) - actionview (= 7.0.8) - activesupport (= 7.0.8) - rack (~> 2.0, >= 2.2.4) + rails-dom-testing (~> 2.2) + actionpack (7.1.1) + actionview (= 7.1.1) + activesupport (= 7.1.1) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8) - actionpack (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.1) + actionpack (= 7.1.1) + activerecord (= 7.1.1) + activestorage (= 7.1.1) + activesupport (= 7.1.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8) - activesupport (= 7.0.8) + actionview (7.1.1) + activesupport (= 7.1.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) active_model_serializers (0.10.14) actionpack (>= 4.1) activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (7.0.8) - activesupport (= 7.0.8) + activejob (7.1.1) + activesupport (= 7.1.1) globalid (>= 0.3.6) - activemodel (7.0.8) - activesupport (= 7.0.8) - activerecord (7.0.8) - activemodel (= 7.0.8) - activesupport (= 7.0.8) - activestorage (7.0.8) - actionpack (= 7.0.8) - activejob (= 7.0.8) - activerecord (= 7.0.8) - activesupport (= 7.0.8) + activemodel (7.1.1) + activesupport (= 7.1.1) + activerecord (7.1.1) + activemodel (= 7.1.1) + activesupport (= 7.1.1) + timeout (>= 0.4.0) + activestorage (7.1.1) + actionpack (= 7.1.1) + activejob (= 7.1.1) + activerecord (= 7.1.1) + activesupport (= 7.1.1) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.8) + activesupport (7.1.1) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) @@ -158,11 +166,12 @@ GEM erubi (~> 1.4) parser (>= 2.4) smart_properties + bigdecimal (3.1.4) bindata (2.4.15) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) blurhash (0.1.7) - bootsnap (1.16.0) + bootsnap (1.17.0) msgpack (~> 1.2) brakeman (6.0.1) browser (5.3.1) @@ -237,6 +246,8 @@ GEM dotenv-rails (2.8.1) dotenv (= 2.8.1) railties (>= 3.2) + drb (2.1.1) + ruby2_keywords ed25519 (1.3.0) elasticsearch (7.13.3) elasticsearch-api (= 7.13.3) @@ -305,8 +316,8 @@ GEM fuubar (2.5.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - globalid (1.1.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) haml (6.2.0) temple (>= 0.8.2) thor @@ -357,7 +368,11 @@ GEM rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) idn-ruby (0.1.5) + io-console (0.6.0) ipaddress (0.8.3) + irb (1.8.1) + rdoc + reline (>= 0.3.8) jmespath (1.6.2) json (2.6.3) json-canonicalization (0.3.2) @@ -434,21 +449,21 @@ GEM azure-storage-blob (~> 2.0.1) hashie (~> 5.0) memory_profiler (1.0.1) - method_source (1.0.0) mime-types (3.5.1) mime-types-data (~> 3.2015) mime-types-data (3.2023.0808) mini_mime (1.1.5) mini_portile2 (2.8.4) minitest (5.20.0) - msgpack (1.7.1) + msgpack (1.7.2) multi_json (1.15.0) multipart-post (2.3.0) + mutex_m (0.1.2) net-http (0.3.2) uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.3.7) + net-imap (0.4.1) date net-protocol net-ldap (0.18.0) @@ -456,7 +471,7 @@ GEM net-protocol net-protocol (0.2.1) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol nio4r (2.5.9) nokogiri (1.15.4) @@ -512,10 +527,12 @@ GEM net-smtp premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) + psych (5.1.1) + stringio public_suffix (5.0.3) puma (6.4.0) nio4r (~> 2.0) - pundit (2.3.0) + pundit (2.3.1) activesupport (>= 3.0.0) raabro (1.4.0) racc (1.7.1) @@ -534,22 +551,27 @@ GEM rack rack-proxy (0.7.6) rack + rack-session (1.0.1) + rack (< 3) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8) - actioncable (= 7.0.8) - actionmailbox (= 7.0.8) - actionmailer (= 7.0.8) - actionpack (= 7.0.8) - actiontext (= 7.0.8) - actionview (= 7.0.8) - activejob (= 7.0.8) - activemodel (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + rackup (1.0.0) + rack (< 3) + webrick + rails (7.1.1) + actioncable (= 7.1.1) + actionmailbox (= 7.1.1) + actionmailer (= 7.1.1) + actionpack (= 7.1.1) + actiontext (= 7.1.1) + actionview (= 7.1.1) + activejob (= 7.1.1) + activemodel (= 7.1.1) + activerecord (= 7.1.1) + activestorage (= 7.1.1) + activesupport (= 7.1.1) bundler (>= 1.15.0) - railties (= 7.0.8) + railties (= 7.1.1) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -564,19 +586,22 @@ GEM rails-i18n (7.0.8) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.0.8) - actionpack (= 7.0.8) - activesupport (= 7.0.8) - method_source + railties (7.1.1) + actionpack (= 7.1.1) + activesupport (= 7.1.1) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.0.6) rdf (3.2.11) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.6.1) rdf (~> 3.2) + rdoc (6.5.0) + psych (>= 4.0.0) redcarpet (3.6.0) redis (4.8.1) redis-namespace (1.11.0) @@ -584,13 +609,15 @@ GEM redlock (1.3.2) redis (>= 3.0.0, < 6.0) regexp_parser (2.8.2) + reline (0.3.9) + io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) rexml (3.2.6) - rotp (6.2.2) + rotp (6.3.0) rouge (4.1.2) rpam2 (4.0.2) rqrcode (2.2.0) @@ -604,7 +631,7 @@ GEM rspec-support (~> 3.12.0) rspec-github (2.4.0) rspec-core (~> 3.0) - rspec-mocks (3.12.5) + rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-rails (6.0.3) @@ -615,13 +642,12 @@ GEM rspec-expectations (~> 3.12) rspec-mocks (~> 3.12) rspec-support (~> 3.12) - rspec-sidekiq (4.0.1) + rspec-sidekiq (4.1.0) rspec-core (~> 3.0) rspec-expectations (~> 3.0) rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.12.1) - rspec_chunked (0.6) rubocop (1.57.1) base64 (~> 0.1.1) json (~> 2.3) @@ -712,7 +738,8 @@ GEM statsd-ruby (1.5.0) stoplight (3.0.2) redlock (~> 1.0) - strong_migrations (0.8.0) + stringio (3.0.8) + strong_migrations (1.3.0) activerecord (>= 5.2) swd (1.3.0) activesupport (>= 3) @@ -783,6 +810,7 @@ GEM rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) + webrick (1.8.1) websocket (1.2.10) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) @@ -804,7 +832,7 @@ DEPENDENCIES better_errors (~> 2.9) binding_of_caller (~> 1.0) blurhash (~> 0.1) - bootsnap (~> 1.16.0) + bootsnap (~> 1.17.0) brakeman (~> 6.0) browser bundler-audit (~> 0.9) @@ -878,7 +906,7 @@ DEPENDENCIES rack-attack (~> 6.6) rack-cors (~> 2.0) rack-test (~> 2.1) - rails (~> 7.0) + rails (~> 7.1.1) rails-controller-testing (~> 1.0) rails-i18n (~> 7.0) rails-settings-cached (~> 0.6)! @@ -890,7 +918,6 @@ DEPENDENCIES rspec-github (~> 2.4) rspec-rails (~> 6.0) rspec-sidekiq (~> 4.0) - rspec_chunked (~> 0.6) rubocop rubocop-capybara rubocop-performance @@ -913,7 +940,7 @@ DEPENDENCIES sprockets-rails (~> 3.4) stackprof stoplight (~> 3.0.1) - strong_migrations (~> 0.8) + strong_migrations (= 1.3.0) test-prof thor (~> 1.2) tty-prompt (~> 0.23) @@ -929,4 +956,4 @@ RUBY VERSION ruby 3.2.2p53 BUNDLED WITH - 2.4.13 + 2.4.20 diff --git a/README.md b/README.md index ce9b5cfbdc161b..62b04f0203d55c 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre ### Requirements -- **PostgreSQL** 9.5+ +- **PostgreSQL** 12+ - **Redis** 4+ - **Ruby** 2.7+ - **Node.js** 16+ diff --git a/SECURITY.md b/SECURITY.md index 3e13377db63eb4..954ff73a247425 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,6 +17,6 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through | ------- | ---------------- | | 4.2.x | Yes | | 4.1.x | Yes | -| 4.0.x | Until 2023-10-31 | +| 4.0.x | No | | 3.5.x | Until 2023-12-31 | | < 3.5 | No | diff --git a/app/controllers/admin/account_actions_controller.rb b/app/controllers/admin/account_actions_controller.rb index e89404b6098e0f..e674bf55a028b0 100644 --- a/app/controllers/admin/account_actions_controller.rb +++ b/app/controllers/admin/account_actions_controller.rb @@ -21,7 +21,7 @@ def create account_action.save! if account_action.with_report? - redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: params[:report_id]) + redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: resource_params[:report_id]) else redirect_to admin_account_path(@account.id) end diff --git a/app/controllers/admin/disputes/appeals_controller.rb b/app/controllers/admin/disputes/appeals_controller.rb index 32e5e2f6fd82ed..5e342409b021cb 100644 --- a/app/controllers/admin/disputes/appeals_controller.rb +++ b/app/controllers/admin/disputes/appeals_controller.rb @@ -20,7 +20,7 @@ def reject authorize @appeal, :approve? log_action :reject, @appeal @appeal.reject!(current_account) - UserMailer.appeal_rejected(@appeal.account.user, @appeal) + UserMailer.appeal_rejected(@appeal.account.user, @appeal).deliver_later redirect_to disputes_strike_path(@appeal.strike) end diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 96c31a38fdfa1e..325b33df80be66 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -33,7 +33,7 @@ def create # Disallow accidentally downgrading a domain block if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block) - @domain_block.save + @domain_block.validate 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) return render :new diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index 667e8eb063513e..05e4605f4e6a46 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -39,6 +39,12 @@ def confirm_captcha show end + def redirect_to_app? + truthy_param?(:redirect_to_app) + end + + helper_method :redirect_to_app? + private def require_captcha_if_needed! @@ -82,7 +88,7 @@ def after_resending_confirmation_instructions_path_for(_resource_name) end def after_confirmation_path_for(_resource_name, user) - if user.created_by_application && truthy_param?(:redirect_to_app) + if user.created_by_application && redirect_to_app? user.created_by_application.confirmation_redirect_uri elsif user_signed_in? web_url('start') diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb index 55ebe1bd649cb9..2dfe5e263af05a 100644 --- a/app/controllers/concerns/cache_concern.rb +++ b/app/controllers/concerns/cache_concern.rb @@ -92,18 +92,10 @@ def serialize_record(record) arguments end - if Rails.gem_version >= Gem::Version.new('7.0') - def attributes_for_database(record) - attributes = record.attributes_for_database - attributes.transform_values! { |attr| attr.is_a?(::ActiveModel::Type::Binary::Data) ? attr.to_s : attr } - attributes - end - else - def attributes_for_database(record) - attributes = record.instance_variable_get(:@attributes).send(:attributes).transform_values(&:value_for_database) - attributes.transform_values! { |attr| attr.is_a?(::ActiveModel::Type::Binary::Data) ? attr.to_s : attr } - attributes - end + def attributes_for_database(record) + attributes = record.attributes_for_database + attributes.transform_values! { |attr| attr.is_a?(::ActiveModel::Type::Binary::Data) ? attr.to_s : attr } + attributes end def deserialize_record(class_name, attributes_from_database, new_record = false) # rubocop:disable Style/OptionalBooleanParameter diff --git a/app/javascript/__mocks__/svg.js b/app/javascript/__mocks__/svg.js new file mode 100644 index 00000000000000..e725dc2da6d8d3 --- /dev/null +++ b/app/javascript/__mocks__/svg.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line import/no-anonymous-default-export +export default 'SvgrURL'; +export const ReactComponent = 'div'; diff --git a/app/javascript/mastodon/actions/account_notes.ts b/app/javascript/mastodon/actions/account_notes.ts index eeef23e3666c50..e524e5235bfe5a 100644 --- a/app/javascript/mastodon/actions/account_notes.ts +++ b/app/javascript/mastodon/actions/account_notes.ts @@ -1,3 +1,4 @@ +import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import { createAppAsyncThunk } from 'mastodon/store/typed_functions'; import api from '../api'; @@ -5,8 +6,7 @@ import api from '../api'; export const submitAccountNote = createAppAsyncThunk( 'account_note/submit', async (args: { id: string; value: string }, { getState }) => { - // TODO: replace `unknown` with `ApiRelationshipJSON` when it is merged - const response = await api(getState).post( + const response = await api(getState).post( `/api/v1/accounts/${args.id}/note`, { comment: args.value, diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 3a85393d6cf273..4a985a41eff0f3 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,5 +1,15 @@ import api, { getLinks } from '../api'; +import { + followAccountSuccess, unfollowAccountSuccess, + authorizeFollowRequestSuccess, rejectFollowRequestSuccess, + followAccountRequest, followAccountFail, + unfollowAccountRequest, unfollowAccountFail, + muteAccountSuccess, unmuteAccountSuccess, + blockAccountSuccess, unblockAccountSuccess, + pinAccountSuccess, unpinAccountSuccess, + fetchRelationshipsSuccess, +} from './accounts_typed'; import { importFetchedAccount, importFetchedAccounts } from './importer'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; @@ -10,36 +20,22 @@ export const ACCOUNT_LOOKUP_REQUEST = 'ACCOUNT_LOOKUP_REQUEST'; export const ACCOUNT_LOOKUP_SUCCESS = 'ACCOUNT_LOOKUP_SUCCESS'; export const ACCOUNT_LOOKUP_FAIL = 'ACCOUNT_LOOKUP_FAIL'; -export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST'; -export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS'; -export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL'; - -export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST'; -export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS'; -export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL'; - export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST'; -export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS'; export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL'; export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST'; -export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS'; export const ACCOUNT_UNBLOCK_FAIL = 'ACCOUNT_UNBLOCK_FAIL'; export const ACCOUNT_MUTE_REQUEST = 'ACCOUNT_MUTE_REQUEST'; -export const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS'; export const ACCOUNT_MUTE_FAIL = 'ACCOUNT_MUTE_FAIL'; export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST'; -export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS'; export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL'; export const ACCOUNT_PIN_REQUEST = 'ACCOUNT_PIN_REQUEST'; -export const ACCOUNT_PIN_SUCCESS = 'ACCOUNT_PIN_SUCCESS'; export const ACCOUNT_PIN_FAIL = 'ACCOUNT_PIN_FAIL'; export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST'; -export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS'; export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL'; export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; @@ -59,7 +55,6 @@ export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS'; export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL'; export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST'; -export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'; export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL'; export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST'; @@ -71,15 +66,15 @@ export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS'; export const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL'; export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST'; -export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS'; export const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL'; export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; -export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL'; export const ACCOUNT_REVEAL = 'ACCOUNT_REVEAL'; +export * from './accounts_typed'; + export function fetchAccount(id) { return (dispatch, getState) => { dispatch(fetchRelationships([id])); @@ -149,12 +144,12 @@ export function followAccount(id, options = { reblogs: true }) { const alreadyFollowing = getState().getIn(['relationships', id, 'following']); const locked = getState().getIn(['accounts', id, 'locked'], false); - dispatch(followAccountRequest(id, locked)); + dispatch(followAccountRequest({ id, locked })); api(getState).post(`/api/v1/accounts/${id}/follow`, options).then(response => { - dispatch(followAccountSuccess(response.data, alreadyFollowing)); + dispatch(followAccountSuccess({relationship: response.data, alreadyFollowing})); }).catch(error => { - dispatch(followAccountFail(error, locked)); + dispatch(followAccountFail({ id, error, locked })); }); }; } @@ -164,74 +159,22 @@ export function unfollowAccount(id) { dispatch(unfollowAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => { - dispatch(unfollowAccountSuccess(response.data, getState().get('statuses'))); + dispatch(unfollowAccountSuccess({relationship: response.data, statuses: getState().get('statuses')})); }).catch(error => { - dispatch(unfollowAccountFail(error)); + dispatch(unfollowAccountFail({ id, error })); }); }; } -export function followAccountRequest(id, locked) { - return { - type: ACCOUNT_FOLLOW_REQUEST, - id, - locked, - skipLoading: true, - }; -} - -export function followAccountSuccess(relationship, alreadyFollowing) { - return { - type: ACCOUNT_FOLLOW_SUCCESS, - relationship, - alreadyFollowing, - skipLoading: true, - }; -} - -export function followAccountFail(error, locked) { - return { - type: ACCOUNT_FOLLOW_FAIL, - error, - locked, - skipLoading: true, - }; -} - -export function unfollowAccountRequest(id) { - return { - type: ACCOUNT_UNFOLLOW_REQUEST, - id, - skipLoading: true, - }; -} - -export function unfollowAccountSuccess(relationship, statuses) { - return { - type: ACCOUNT_UNFOLLOW_SUCCESS, - relationship, - statuses, - skipLoading: true, - }; -} - -export function unfollowAccountFail(error) { - return { - type: ACCOUNT_UNFOLLOW_FAIL, - error, - skipLoading: true, - }; -} - export function blockAccount(id) { return (dispatch, getState) => { dispatch(blockAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/block`).then(response => { // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers - dispatch(blockAccountSuccess(response.data, getState().get('statuses'))); + dispatch(blockAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') })); }).catch(error => { - dispatch(blockAccountFail(id, error)); + dispatch(blockAccountFail({ id, error })); }); }; } @@ -241,9 +184,9 @@ export function unblockAccount(id) { dispatch(unblockAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => { - dispatch(unblockAccountSuccess(response.data)); + dispatch(unblockAccountSuccess({ relationship: response.data })); }).catch(error => { - dispatch(unblockAccountFail(id, error)); + dispatch(unblockAccountFail({ id, error })); }); }; } @@ -254,15 +197,6 @@ export function blockAccountRequest(id) { id, }; } - -export function blockAccountSuccess(relationship, statuses) { - return { - type: ACCOUNT_BLOCK_SUCCESS, - relationship, - statuses, - }; -} - export function blockAccountFail(error) { return { type: ACCOUNT_BLOCK_FAIL, @@ -277,13 +211,6 @@ export function unblockAccountRequest(id) { }; } -export function unblockAccountSuccess(relationship) { - return { - type: ACCOUNT_UNBLOCK_SUCCESS, - relationship, - }; -} - export function unblockAccountFail(error) { return { type: ACCOUNT_UNBLOCK_FAIL, @@ -298,9 +225,9 @@ export function muteAccount(id, notifications, duration=0) { api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications, duration }).then(response => { // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers - dispatch(muteAccountSuccess(response.data, getState().get('statuses'))); + dispatch(muteAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') })); }).catch(error => { - dispatch(muteAccountFail(id, error)); + dispatch(muteAccountFail({ id, error })); }); }; } @@ -310,9 +237,9 @@ export function unmuteAccount(id) { dispatch(unmuteAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/unmute`).then(response => { - dispatch(unmuteAccountSuccess(response.data)); + dispatch(unmuteAccountSuccess({ relationship: response.data })); }).catch(error => { - dispatch(unmuteAccountFail(id, error)); + dispatch(unmuteAccountFail({ id, error })); }); }; } @@ -324,14 +251,6 @@ export function muteAccountRequest(id) { }; } -export function muteAccountSuccess(relationship, statuses) { - return { - type: ACCOUNT_MUTE_SUCCESS, - relationship, - statuses, - }; -} - export function muteAccountFail(error) { return { type: ACCOUNT_MUTE_FAIL, @@ -346,13 +265,6 @@ export function unmuteAccountRequest(id) { }; } -export function unmuteAccountSuccess(relationship) { - return { - type: ACCOUNT_UNMUTE_SUCCESS, - relationship, - }; -} - export function unmuteAccountFail(error) { return { type: ACCOUNT_UNMUTE_FAIL, @@ -549,7 +461,7 @@ export function fetchRelationships(accountIds) { dispatch(fetchRelationshipsRequest(newAccountIds)); api(getState).get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { - dispatch(fetchRelationshipsSuccess(response.data)); + dispatch(fetchRelationshipsSuccess({ relationships: response.data })); }).catch(error => { dispatch(fetchRelationshipsFail(error)); }); @@ -564,14 +476,6 @@ export function fetchRelationshipsRequest(ids) { }; } -export function fetchRelationshipsSuccess(relationships) { - return { - type: RELATIONSHIPS_FETCH_SUCCESS, - relationships, - skipLoading: true, - }; -} - export function fetchRelationshipsFail(error) { return { type: RELATIONSHIPS_FETCH_FAIL, @@ -659,7 +563,7 @@ export function authorizeFollowRequest(id) { api(getState) .post(`/api/v1/follow_requests/${id}/authorize`) - .then(() => dispatch(authorizeFollowRequestSuccess(id))) + .then(() => dispatch(authorizeFollowRequestSuccess({ id }))) .catch(error => dispatch(authorizeFollowRequestFail(id, error))); }; } @@ -671,13 +575,6 @@ export function authorizeFollowRequestRequest(id) { }; } -export function authorizeFollowRequestSuccess(id) { - return { - type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, - id, - }; -} - export function authorizeFollowRequestFail(id, error) { return { type: FOLLOW_REQUEST_AUTHORIZE_FAIL, @@ -693,7 +590,7 @@ export function rejectFollowRequest(id) { api(getState) .post(`/api/v1/follow_requests/${id}/reject`) - .then(() => dispatch(rejectFollowRequestSuccess(id))) + .then(() => dispatch(rejectFollowRequestSuccess({ id }))) .catch(error => dispatch(rejectFollowRequestFail(id, error))); }; } @@ -705,13 +602,6 @@ export function rejectFollowRequestRequest(id) { }; } -export function rejectFollowRequestSuccess(id) { - return { - type: FOLLOW_REQUEST_REJECT_SUCCESS, - id, - }; -} - export function rejectFollowRequestFail(id, error) { return { type: FOLLOW_REQUEST_REJECT_FAIL, @@ -725,7 +615,7 @@ export function pinAccount(id) { dispatch(pinAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => { - dispatch(pinAccountSuccess(response.data)); + dispatch(pinAccountSuccess({ relationship: response.data })); }).catch(error => { dispatch(pinAccountFail(error)); }); @@ -737,7 +627,7 @@ export function unpinAccount(id) { dispatch(unpinAccountRequest(id)); api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => { - dispatch(unpinAccountSuccess(response.data)); + dispatch(unpinAccountSuccess({ relationship: response.data })); }).catch(error => { dispatch(unpinAccountFail(error)); }); @@ -751,13 +641,6 @@ export function pinAccountRequest(id) { }; } -export function pinAccountSuccess(relationship) { - return { - type: ACCOUNT_PIN_SUCCESS, - relationship, - }; -} - export function pinAccountFail(error) { return { type: ACCOUNT_PIN_FAIL, @@ -772,21 +655,9 @@ export function unpinAccountRequest(id) { }; } -export function unpinAccountSuccess(relationship) { - return { - type: ACCOUNT_UNPIN_SUCCESS, - relationship, - }; -} - export function unpinAccountFail(error) { return { type: ACCOUNT_UNPIN_FAIL, error, }; } - -export const revealAccount = id => ({ - type: ACCOUNT_REVEAL, - id, -}); diff --git a/app/javascript/mastodon/actions/accounts_typed.ts b/app/javascript/mastodon/actions/accounts_typed.ts new file mode 100644 index 00000000000000..b908e7528eeb60 --- /dev/null +++ b/app/javascript/mastodon/actions/accounts_typed.ts @@ -0,0 +1,97 @@ +import { createAction } from '@reduxjs/toolkit'; + +import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; +import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; + +export const revealAccount = createAction<{ + id: string; +}>('accounts/revealAccount'); + +export const importAccounts = createAction<{ accounts: ApiAccountJSON[] }>( + 'accounts/importAccounts', +); + +function actionWithSkipLoadingTrue(args: Args) { + return { + payload: { + ...args, + skipLoading: true, + }, + }; +} + +export const followAccountSuccess = createAction( + 'accounts/followAccountSuccess', + actionWithSkipLoadingTrue<{ + relationship: ApiRelationshipJSON; + alreadyFollowing: boolean; + }>, +); + +export const unfollowAccountSuccess = createAction( + 'accounts/unfollowAccountSuccess', + actionWithSkipLoadingTrue<{ + relationship: ApiRelationshipJSON; + statuses: unknown; + alreadyFollowing?: boolean; + }>, +); + +export const authorizeFollowRequestSuccess = createAction<{ id: string }>( + 'accounts/followRequestAuthorizeSuccess', +); + +export const rejectFollowRequestSuccess = createAction<{ id: string }>( + 'accounts/followRequestRejectSuccess', +); + +export const followAccountRequest = createAction( + 'accounts/followRequest', + actionWithSkipLoadingTrue<{ id: string; locked: boolean }>, +); + +export const followAccountFail = createAction( + 'accounts/followFail', + actionWithSkipLoadingTrue<{ id: string; error: string; locked: boolean }>, +); + +export const unfollowAccountRequest = createAction( + 'accounts/unfollowRequest', + actionWithSkipLoadingTrue<{ id: string }>, +); + +export const unfollowAccountFail = createAction( + 'accounts/unfollowFail', + actionWithSkipLoadingTrue<{ id: string; error: string }>, +); + +export const blockAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; + statuses: unknown; +}>('accounts/blockSuccess'); + +export const unblockAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; +}>('accounts/unblockSuccess'); + +export const muteAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; + statuses: unknown; +}>('accounts/muteSuccess'); + +export const unmuteAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; +}>('accounts/unmuteSuccess'); + +export const pinAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; +}>('accounts/pinSuccess'); + +export const unpinAccountSuccess = createAction<{ + relationship: ApiRelationshipJSON; +}>('accounts/unpinSuccess'); + +export const fetchRelationshipsSuccess = createAction( + 'relationships/fetchSuccess', + actionWithSkipLoadingTrue<{ relationships: ApiRelationshipJSON[] }>, +); diff --git a/app/javascript/mastodon/actions/domain_blocks.js b/app/javascript/mastodon/actions/domain_blocks.js index d06de20a2d17f5..718002613f4053 100644 --- a/app/javascript/mastodon/actions/domain_blocks.js +++ b/app/javascript/mastodon/actions/domain_blocks.js @@ -1,11 +1,13 @@ import api, { getLinks } from '../api'; +import { blockDomainSuccess, unblockDomainSuccess } from "./domain_blocks_typed"; + +export * from "./domain_blocks_typed"; + export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST'; -export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS'; export const DOMAIN_BLOCK_FAIL = 'DOMAIN_BLOCK_FAIL'; export const DOMAIN_UNBLOCK_REQUEST = 'DOMAIN_UNBLOCK_REQUEST'; -export const DOMAIN_UNBLOCK_SUCCESS = 'DOMAIN_UNBLOCK_SUCCESS'; export const DOMAIN_UNBLOCK_FAIL = 'DOMAIN_UNBLOCK_FAIL'; export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST'; @@ -24,7 +26,7 @@ export function blockDomain(domain) { const at_domain = '@' + domain; const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); - dispatch(blockDomainSuccess(domain, accounts)); + dispatch(blockDomainSuccess({ domain, accounts })); }).catch(err => { dispatch(blockDomainFail(domain, err)); }); @@ -38,14 +40,6 @@ export function blockDomainRequest(domain) { }; } -export function blockDomainSuccess(domain, accounts) { - return { - type: DOMAIN_BLOCK_SUCCESS, - domain, - accounts, - }; -} - export function blockDomainFail(domain, error) { return { type: DOMAIN_BLOCK_FAIL, @@ -61,7 +55,7 @@ export function unblockDomain(domain) { api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => { const at_domain = '@' + domain; const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); - dispatch(unblockDomainSuccess(domain, accounts)); + dispatch(unblockDomainSuccess({ domain, accounts })); }).catch(err => { dispatch(unblockDomainFail(domain, err)); }); @@ -75,14 +69,6 @@ export function unblockDomainRequest(domain) { }; } -export function unblockDomainSuccess(domain, accounts) { - return { - type: DOMAIN_UNBLOCK_SUCCESS, - domain, - accounts, - }; -} - export function unblockDomainFail(domain, error) { return { type: DOMAIN_UNBLOCK_FAIL, diff --git a/app/javascript/mastodon/actions/domain_blocks_typed.ts b/app/javascript/mastodon/actions/domain_blocks_typed.ts new file mode 100644 index 00000000000000..08e0b4a1788e60 --- /dev/null +++ b/app/javascript/mastodon/actions/domain_blocks_typed.ts @@ -0,0 +1,13 @@ +import { createAction } from '@reduxjs/toolkit'; + +import type { Account } from 'mastodon/models/account'; + +export const blockDomainSuccess = createAction<{ + domain: string; + accounts: Account[]; +}>('domain_blocks/blockSuccess'); + +export const unblockDomainSuccess = createAction<{ + domain: string; + accounts: Account[]; +}>('domain_blocks/unblockSuccess'); diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index 369be6b8fbc0ca..16f191b5846f63 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -1,7 +1,7 @@ -import { normalizeAccount, normalizeStatus, normalizePoll } from './normalizer'; +import { importAccounts } from '../accounts_typed'; + +import { normalizeStatus, normalizePoll } from './normalizer'; -export const ACCOUNT_IMPORT = 'ACCOUNT_IMPORT'; -export const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT'; export const STATUS_IMPORT = 'STATUS_IMPORT'; export const STATUSES_IMPORT = 'STATUSES_IMPORT'; export const POLLS_IMPORT = 'POLLS_IMPORT'; @@ -13,14 +13,6 @@ function pushUnique(array, object) { } } -export function importAccount(account) { - return { type: ACCOUNT_IMPORT, account }; -} - -export function importAccounts(accounts) { - return { type: ACCOUNTS_IMPORT, accounts }; -} - export function importStatus(status) { return { type: STATUS_IMPORT, status }; } @@ -45,7 +37,7 @@ export function importFetchedAccounts(accounts) { const normalAccounts = []; function processAccount(account) { - pushUnique(normalAccounts, normalizeAccount(account)); + pushUnique(normalAccounts, account); if (account.moved) { processAccount(account.moved); @@ -54,7 +46,7 @@ export function importFetchedAccounts(accounts) { accounts.forEach(processAccount); - return importAccounts(normalAccounts); + return importAccounts({ accounts: normalAccounts }); } export function importFetchedStatus(status) { diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index a72142a86f70bb..b5a30343e488c4 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -2,7 +2,6 @@ import escapeTextContentForBrowser from 'escape-html'; import emojify from '../../features/emoji/emoji'; import { expandSpoilers } from '../../initial_state'; -import { unescapeHTML } from '../../utils/html'; const domParser = new DOMParser(); @@ -17,32 +16,6 @@ export function searchTextFromRawStatus (status) { return domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; } -export function normalizeAccount(account) { - account = { ...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); - account.note_emojified = emojify(account.note, emojiMap); - account.note_plain = unescapeHTML(account.note); - - if (account.fields) { - account.fields = account.fields.map(pair => ({ - ...pair, - name_emojified: emojify(escapeTextContentForBrowser(pair.name), emojiMap), - value_emojified: emojify(pair.value, emojiMap), - value_plain: unescapeHTML(pair.value), - })); - } - - if (account.moved) { - account.moved = account.moved.id; - } - - return account; -} - export function normalizeFilterResult(result) { const normalResult = { ...result }; diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 02fe10ba56e4a0..878ff5d89d8946 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -18,10 +18,12 @@ import { importFetchedStatuses, } from './importer'; import { submitMarkers } from './markers'; +import { notificationsUpdate } from "./notifications_typed"; import { register as registerPushNotifications } from './push_notifications'; import { saveSettings } from './settings'; -export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; +export * from "./notifications_typed"; + export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; @@ -95,12 +97,8 @@ export function updateNotifications(notification, intlMessages, intlLocale) { dispatch(importFetchedAccount(notification.report.target_account)); } - dispatch({ - type: NOTIFICATIONS_UPDATE, - notification, - usePendingItems: preferPendingItems, - meta: (playSound && !filtered) ? { sound: 'boop' } : undefined, - }); + + dispatch(notificationsUpdate(notification, preferPendingItems, playSound && !filtered)); fetchRelatedRelationships(dispatch, [notification]); } else if (playSound && !filtered) { diff --git a/app/javascript/mastodon/actions/notifications_typed.ts b/app/javascript/mastodon/actions/notifications_typed.ts new file mode 100644 index 00000000000000..7e51fa51e7b7e8 --- /dev/null +++ b/app/javascript/mastodon/actions/notifications_typed.ts @@ -0,0 +1,23 @@ +import { createAction } from '@reduxjs/toolkit'; + +import type { ApiAccountJSON } from '../api_types/accounts'; +// To be replaced once ApiNotificationJSON type exists +interface FakeApiNotificationJSON { + type: string; + account: ApiAccountJSON; +} + +export const notificationsUpdate = createAction( + 'notifications/update', + ({ + playSound, + ...args + }: { + notification: FakeApiNotificationJSON; + usePendingItems: boolean; + playSound: boolean; + }) => ({ + payload: args, + meta: { playSound: playSound ? { sound: 'boop' } : undefined }, + }), +); diff --git a/app/javascript/mastodon/actions/store.js b/app/javascript/mastodon/actions/store.js index 682b0f5db7e4a0..8ab75cdc444620 100644 --- a/app/javascript/mastodon/actions/store.js +++ b/app/javascript/mastodon/actions/store.js @@ -11,6 +11,7 @@ const convertState = rawState => fromJS(rawState, (k, v) => Iterable.isIndexed(v) ? v.toList() : v.toMap()); + export function hydrateStore(rawState) { return dispatch => { const state = convertState(rawState); diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts index 9d740c96de6977..ce55dc604ad001 100644 --- a/app/javascript/mastodon/api_types/accounts.ts +++ b/app/javascript/mastodon/api_types/accounts.ts @@ -31,9 +31,9 @@ export interface ApiAccountJSON { id: string; last_status_at: string; locked: boolean; - noindex: boolean; + noindex?: boolean; note: string; - roles: ApiAccountJSON[]; + roles?: ApiAccountJSON[]; statuses_count: number; uri: string; url: string; diff --git a/app/javascript/mastodon/components/__tests__/button-test.jsx b/app/javascript/mastodon/components/__tests__/button-test.jsx index ad7a0c49cab724..f38ff6a7ddf8c3 100644 --- a/app/javascript/mastodon/components/__tests__/button-test.jsx +++ b/app/javascript/mastodon/components/__tests__/button-test.jsx @@ -1,6 +1,7 @@ -import { render, fireEvent, screen } from '@testing-library/react'; import renderer from 'react-test-renderer'; +import { render, fireEvent, screen } from 'mastodon/test_helpers'; + import { Button } from '../button'; describe('