diff --git a/.env.production.sample b/.env.production.sample index 0bf01bdc3..0b458a1aa 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,5 +1,5 @@ # This is a sample configuration file. You can generate your configuration -# with the `rake mastodon:setup` interactive setup wizard, but to customize +# with the `bundle exec rails mastodon:setup` interactive setup wizard, but to customize # your setup even further, you'll need to edit it manually. This sample does # not demonstrate all available configuration options. Please look at # https://docs.joinmastodon.org/admin/config/ for the full documentation. @@ -40,14 +40,14 @@ ES_PASS=password # Secrets # ------- -# Make sure to use `rake secret` to generate secrets +# Make sure to use `bundle exec rails secret` to generate secrets # ------- SECRET_KEY_BASE= OTP_SECRET= # Web Push # -------- -# Generate with `rake mastodon:webpush:generate_vapid_key` +# Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` # -------- VAPID_PRIVATE_KEY= VAPID_PUBLIC_KEY= diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 03787dfac..2cf7bec8e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -14,9 +14,6 @@ // to `null` after any other rule set it to something. dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).', postUpdateOptions: ['yarnDedupeHighest'], - lockFileMaintenance: { - enabled: true, - }, packageRules: [ { // Require Dependency Dashboard Approval for major version bumps of these node packages diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index dd71fd253..ef898968d 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -132,15 +132,17 @@ jobs: additional-system-dependencies: ffmpeg libpam-dev - name: Load database schema - run: './bin/rails db:create db:schema:load db:seed' + run: | + bin/rails db:setup + bin/flatware fan bin/rails db:test:prepare - - run: bin/rspec + - run: bin/flatware rspec -r ./spec/flatware_helper.rb - name: Upload coverage reports to Codecov if: matrix.ruby-version == '.ruby-version' uses: codecov/codecov-action@v4 with: - files: coverage/lcov/mastodon.lcov + files: coverage/lcov/*.lcov env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.rubocop/rails.yml b/.rubocop/rails.yml index 4e08f1ab9..ae31c1f26 100644 --- a/.rubocop/rails.yml +++ b/.rubocop/rails.yml @@ -1,4 +1,7 @@ --- +Rails/BulkChangeTable: + Enabled: false # Conflicts with strong_migrations features + Rails/FilePath: EnforcedStyle: arguments @@ -18,6 +21,3 @@ Rails/RakeEnvironment: Rails/SkipsModelValidations: Enabled: false - -Rails/UnusedIgnoredColumns: - Enabled: false # Preserve ability to migrate from arbitrary old versions diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b24d6f1..7c3d96ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to this project will be documented in this file. +## [4.2.10] - 2024-07-04 + +### Security + +- Fix incorrect permission checking on multiple API endpoints ([GHSA-58x8-3qxw-6hm7](https://github.com/mastodon/mastodon/security/advisories/GHSA-58x8-3qxw-6hm7)) +- Fix incorrect authorship checking when processing some activities (CVE-2024-37903, [GHSA-xjvf-fm67-4qc3](https://github.com/mastodon/mastodon/security/advisories/GHSA-xjvf-fm67-4qc3)) +- Fix ongoing streaming sessions not being invalidated when application tokens get revoked ([GHSA-vp5r-5pgw-jwqx](https://github.com/mastodon/mastodon/security/advisories/GHSA-vp5r-5pgw-jwqx)) +- Update dependencies + +### Added + +- Add yarn version specification to avoid confusion with Yarn 3 and Yarn 4 + +### Changed + +- Change preview cards generation to skip unusually long URLs ([oneiros](https://github.com/mastodon/mastodon/pull/30854)) +- Change search modifiers to be case-insensitive ([Gargron](https://github.com/mastodon/mastodon/pull/30865)) +- Change `STATSD_ADDR` handling to emit a warning rather than crashing if the address is unreachable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30691)) +- Change PWA start URL from `/home` to `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27377)) + +### Removed + +- Removed dependency on `posix-spawn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18559)) + +### Fixed + +- Fix scheduled statuses scheduled in less than 5 minutes being immediately published ([danielmbrasil](https://github.com/mastodon/mastodon/pull/30584)) +- Fix encoding detection for link cards ([oneiros](https://github.com/mastodon/mastodon/pull/30780)) +- Fix `/admin/accounts/:account_id/statuses/:id` for edited posts with media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30819)) +- Fix duplicate `@context` attribute in user archive export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30653)) + ## [4.2.9] - 2024-05-30 ### Security diff --git a/Gemfile b/Gemfile index f2d7d098d..ef52d50ca 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ gem 'ruby-vips', '~> 2.2', require: false gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.8' gem 'bootsnap', '~> 1.18.0', require: false -gem 'browser' +gem 'browser', '< 6' # https://github.com/fnando/browser/issues/543 gem 'charlock_holmes', '~> 0.7.7' gem 'chewy', '~> 7.3' gem 'devise', '~> 4.9' @@ -100,8 +100,6 @@ gem 'json-ld' gem 'json-ld-preloaded', '~> 3.2' gem 'rdf-normalize', '~> 0.5' -gem 'private_address_check', '~> 0.5' - gem 'opentelemetry-api', '~> 1.2.5' group :opentelemetry do @@ -116,13 +114,16 @@ group :opentelemetry do gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false gem 'opentelemetry-instrumentation-pg', '~> 0.27.1', require: false gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false - gem 'opentelemetry-instrumentation-rails', '~> 0.30.0', require: false + gem 'opentelemetry-instrumentation-rails', '~> 0.31.0', require: false gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false gem 'opentelemetry-sdk', '~> 1.4', require: false end group :test do + # Enable usage of all available CPUs/cores during spec runs + gem 'flatware-rspec' + # 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 0fe1c03b2..c0fa8a603 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,19 +100,19 @@ GEM attr_required (1.0.2) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.940.0) - aws-sdk-core (3.197.0) + aws-partitions (1.950.0) + aws-sdk-core (3.201.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.83.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.3) - aws-sdk-core (~> 3, >= 3.197.0) + aws-sdk-kms (1.88.0) + aws-sdk-core (~> 3, >= 3.201.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.156.0) + aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.5) aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) azure-storage-blob (2.0.3) @@ -159,7 +159,7 @@ GEM case_transform (0.2) activesupport cbor (0.5.9.8) - charlock_holmes (0.7.7) + charlock_holmes (0.7.8) chewy (7.6.0) activesupport (>= 5.2) elasticsearch (>= 7.14.0, < 8) @@ -208,7 +208,7 @@ GEM activerecord (>= 4.2, < 8) docile (1.4.0) domain_name (0.6.20240107) - doorkeeper (5.6.9) + doorkeeper (5.7.1) railties (>= 5) dotenv (3.1.2) drb (2.2.1) @@ -264,6 +264,11 @@ GEM ffi-compiler (1.3.2) ffi (>= 1.15.5) rake + flatware (2.3.2) + thor (< 2.0) + flatware-rspec (2.3.2) + flatware (= 2.3.2) + rspec (>= 3.6) fog-core (2.4.0) builder excon (~> 0.71) @@ -426,7 +431,7 @@ GEM mime-types-data (3.2024.0604) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.23.1) + minitest (5.24.1) msgpack (1.7.2) multi_json (1.15.0) multipart-post (2.4.0) @@ -511,7 +516,7 @@ GEM opentelemetry-api (~> 1.0) opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-active_job (0.7.1) + opentelemetry-instrumentation-active_job (0.7.2) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-active_model_serializers (0.20.1) @@ -520,7 +525,7 @@ GEM opentelemetry-instrumentation-active_record (0.7.2) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-active_support (0.5.1) + opentelemetry-instrumentation-active_support (0.6.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (0.22.3) @@ -532,7 +537,7 @@ GEM opentelemetry-instrumentation-excon (0.22.3) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-faraday (0.24.4) + opentelemetry-instrumentation-faraday (0.24.5) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-http (0.23.3) @@ -551,19 +556,19 @@ GEM opentelemetry-instrumentation-rack (0.24.5) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-rails (0.30.2) + opentelemetry-instrumentation-rails (0.31.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-action_mailer (~> 0.1.0) opentelemetry-instrumentation-action_pack (~> 0.9.0) opentelemetry-instrumentation-action_view (~> 0.7.0) opentelemetry-instrumentation-active_job (~> 0.7.0) opentelemetry-instrumentation-active_record (~> 0.7.0) - opentelemetry-instrumentation-active_support (~> 0.5.0) + opentelemetry-instrumentation-active_support (~> 0.6.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-redis (0.25.6) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-sidekiq (0.25.5) + opentelemetry-instrumentation-sidekiq (0.25.6) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-registry (0.3.1) @@ -595,7 +600,6 @@ GEM actionmailer (>= 3) net-smtp premailer (~> 1.7, >= 1.7.9) - private_address_check (0.5.0) propshaft (0.9.0) actionpack (>= 7.0.0) activesupport (>= 7.0.0) @@ -692,7 +696,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.0) + rexml (3.3.1) strscan rotp (6.3.0) rouge (4.2.1) @@ -701,6 +705,10 @@ GEM chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) rspec-core (3.13.0) rspec-support (~> 3.13.0) rspec-expectations (3.13.1) @@ -743,12 +751,12 @@ GEM rubocop-performance (1.21.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.25.0) + rubocop-rails (2.25.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rspec (3.0.1) + rubocop-rspec (3.0.2) rubocop (~> 1.61) rubocop-rspec_rails (2.30.0) rubocop (~> 1.61) @@ -785,10 +793,10 @@ GEM redis (>= 4.5.0, < 5) sidekiq-bulk (0.2.0) sidekiq - sidekiq-scheduler (5.0.3) + sidekiq-scheduler (5.0.5) rufus-scheduler (~> 3.2) sidekiq (>= 6, < 8) - tilt (>= 1.4.0) + tilt (>= 1.4.0, < 3) sidekiq-unique-jobs (7.1.33) brpoplpush-redis_script (> 0.1.1, <= 2.0.0) concurrent-ruby (~> 1.0, >= 1.0.5) @@ -825,7 +833,7 @@ GEM unicode-display_width (>= 1.1.1, < 3) terrapin (1.0.1) climate_control - test-prof (1.3.3) + test-prof (1.3.3.1) thor (1.3.1) tilt (2.3.0) timeout (0.4.1) @@ -908,7 +916,7 @@ DEPENDENCIES blurhash (~> 0.1) bootsnap (~> 1.18.0) brakeman (~> 6.0) - browser + browser (< 6) bundler-audit (~> 0.9) capybara (~> 3.39) charlock_holmes (~> 0.7.7) @@ -933,6 +941,7 @@ DEPENDENCIES faker (~> 3.2) fast_blank (~> 1.0) fastimage + flatware-rspec fog-core (<= 2.4.0) fog-openstack (~> 1.0) fuubar (~> 2.5) @@ -985,7 +994,7 @@ DEPENDENCIES opentelemetry-instrumentation-net_http (~> 0.22.4) opentelemetry-instrumentation-pg (~> 0.27.1) opentelemetry-instrumentation-rack (~> 0.24.1) - opentelemetry-instrumentation-rails (~> 0.30.0) + opentelemetry-instrumentation-rails (~> 0.31.0) opentelemetry-instrumentation-redis (~> 0.25.3) opentelemetry-instrumentation-sidekiq (~> 0.25.2) opentelemetry-sdk (~> 1.4) @@ -994,7 +1003,6 @@ DEPENDENCIES pg (~> 1.5) pghero premailer-rails - private_address_check (~> 0.5) propshaft public_suffix (~> 6.0) puma (~> 6.3) diff --git a/app/controllers/api/v1/scheduled_statuses_controller.rb b/app/controllers/api/v1/scheduled_statuses_controller.rb index 45ee58651..c62305d71 100644 --- a/app/controllers/api/v1/scheduled_statuses_controller.rb +++ b/app/controllers/api/v1/scheduled_statuses_controller.rb @@ -6,6 +6,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy] + before_action :require_user! before_action :set_statuses, only: :index before_action :set_status, except: :index diff --git a/app/controllers/api/v1/statuses/translations_controller.rb b/app/controllers/api/v1/statuses/translations_controller.rb index 7d406b0a3..8cf495f78 100644 --- a/app/controllers/api/v1/statuses/translations_controller.rb +++ b/app/controllers/api/v1/statuses/translations_controller.rb @@ -2,6 +2,7 @@ class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController before_action -> { doorkeeper_authorize! :read, :'read:statuses' } + before_action :require_user! before_action :set_translation rescue_from TranslationService::NotConfiguredError, with: :not_found diff --git a/app/controllers/api/v1/timelines/base_controller.rb b/app/controllers/api/v1/timelines/base_controller.rb index e79eba79e..1dba4a5bb 100644 --- a/app/controllers/api/v1/timelines/base_controller.rb +++ b/app/controllers/api/v1/timelines/base_controller.rb @@ -3,8 +3,14 @@ class Api::V1::Timelines::BaseController < Api::BaseController after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + before_action :require_user!, if: :require_auth? + private + def require_auth? + !Setting.timeline_preview + end + def pagination_collection @statuses end diff --git a/app/controllers/api/v1/timelines/link_controller.rb b/app/controllers/api/v1/timelines/link_controller.rb index af962c430..37ed084f0 100644 --- a/app/controllers/api/v1/timelines/link_controller.rb +++ b/app/controllers/api/v1/timelines/link_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? + before_action -> { authorize_if_got_token! :read, :'read:statuses' } before_action :set_preview_card before_action :set_statuses @@ -17,10 +17,6 @@ class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController private - def require_auth? - !Setting.timeline_preview - end - def set_preview_card @preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url]) end diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb index d164854d6..029e8fc2c 100644 --- a/app/controllers/api/v1/timelines/public_controller.rb +++ b/app/controllers/api/v1/timelines/public_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController - before_action :require_user!, only: [:show], if: :require_auth? + before_action -> { authorize_if_got_token! :read, :'read:statuses' } PERMITTED_PARAMS = %i(local remote limit only_media).freeze @@ -13,10 +13,6 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController private - def require_auth? - !Setting.timeline_preview - end - def load_statuses preloaded_public_statuses_page end diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index 3bf8f374e..2b097aab0 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? + before_action -> { authorize_if_got_token! :read, :'read:statuses' } before_action :load_tag PERMITTED_PARAMS = %i(local limit only_media).freeze diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 8440df6b7..7bb22453c 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -17,6 +17,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio def destroy Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner) + Doorkeeper::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner) super end diff --git a/app/javascript/hooks/useLinks.ts b/app/javascript/hooks/useLinks.ts new file mode 100644 index 000000000..f08b9500d --- /dev/null +++ b/app/javascript/hooks/useLinks.ts @@ -0,0 +1,61 @@ +import { useCallback } from 'react'; + +import { useHistory } from 'react-router-dom'; + +import { openURL } from 'mastodon/actions/search'; +import { useAppDispatch } from 'mastodon/store'; + +const isMentionClick = (element: HTMLAnchorElement) => + element.classList.contains('mention'); + +const isHashtagClick = (element: HTMLAnchorElement) => + element.textContent?.[0] === '#' || + element.previousSibling?.textContent?.endsWith('#'); + +export const useLinks = () => { + const history = useHistory(); + const dispatch = useAppDispatch(); + + const handleHashtagClick = useCallback( + (element: HTMLAnchorElement) => { + const { textContent } = element; + + if (!textContent) return; + + history.push(`/tags/${textContent.replace(/^#/, '')}`); + }, + [history], + ); + + const handleMentionClick = useCallback( + (element: HTMLAnchorElement) => { + dispatch( + openURL(element.href, history, () => { + window.location.href = element.href; + }), + ); + }, + [dispatch, history], + ); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + const target = (e.target as HTMLElement).closest('a'); + + if (!target || e.button !== 0 || e.ctrlKey || e.metaKey) { + return; + } + + if (isMentionClick(target)) { + e.preventDefault(); + handleMentionClick(target); + } else if (isHashtagClick(target)) { + e.preventDefault(); + handleHashtagClick(target); + } + }, + [handleMentionClick, handleHashtagClick], + ); + + return handleClick; +}; diff --git a/app/javascript/hooks/useTimeout.ts b/app/javascript/hooks/useTimeout.ts new file mode 100644 index 000000000..bb1e8848d --- /dev/null +++ b/app/javascript/hooks/useTimeout.ts @@ -0,0 +1,44 @@ +import { useRef, useCallback, useEffect } from 'react'; + +export const useTimeout = () => { + const timeoutRef = useRef>(); + const callbackRef = useRef<() => void>(); + + const set = useCallback((callback: () => void, delay: number) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + callbackRef.current = callback; + timeoutRef.current = setTimeout(callback, delay); + }, []); + + const delay = useCallback((delay: number) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + if (!callbackRef.current) { + return; + } + + timeoutRef.current = setTimeout(callbackRef.current, delay); + }, []); + + const cancel = useCallback(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = undefined; + callbackRef.current = undefined; + } + }, []); + + useEffect( + () => () => { + cancel(); + }, + [cancel], + ); + + return [set, cancel, delay] as const; +}; diff --git a/app/javascript/mastodon/actions/directory.js b/app/javascript/mastodon/actions/directory.js deleted file mode 100644 index 7a0748029..000000000 --- a/app/javascript/mastodon/actions/directory.js +++ /dev/null @@ -1,62 +0,0 @@ -import api from '../api'; - -import { fetchRelationships } from './accounts'; -import { importFetchedAccounts } from './importer'; - -export const DIRECTORY_FETCH_REQUEST = 'DIRECTORY_FETCH_REQUEST'; -export const DIRECTORY_FETCH_SUCCESS = 'DIRECTORY_FETCH_SUCCESS'; -export const DIRECTORY_FETCH_FAIL = 'DIRECTORY_FETCH_FAIL'; - -export const DIRECTORY_EXPAND_REQUEST = 'DIRECTORY_EXPAND_REQUEST'; -export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS'; -export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL'; - -export const fetchDirectory = params => (dispatch) => { - dispatch(fetchDirectoryRequest()); - - api().get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => { - dispatch(importFetchedAccounts(data)); - dispatch(fetchDirectorySuccess(data)); - dispatch(fetchRelationships(data.map(x => x.id))); - }).catch(error => dispatch(fetchDirectoryFail(error))); -}; - -export const fetchDirectoryRequest = () => ({ - type: DIRECTORY_FETCH_REQUEST, -}); - -export const fetchDirectorySuccess = accounts => ({ - type: DIRECTORY_FETCH_SUCCESS, - accounts, -}); - -export const fetchDirectoryFail = error => ({ - type: DIRECTORY_FETCH_FAIL, - error, -}); - -export const expandDirectory = params => (dispatch, getState) => { - dispatch(expandDirectoryRequest()); - - const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size; - - api().get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => { - dispatch(importFetchedAccounts(data)); - dispatch(expandDirectorySuccess(data)); - dispatch(fetchRelationships(data.map(x => x.id))); - }).catch(error => dispatch(expandDirectoryFail(error))); -}; - -export const expandDirectoryRequest = () => ({ - type: DIRECTORY_EXPAND_REQUEST, -}); - -export const expandDirectorySuccess = accounts => ({ - type: DIRECTORY_EXPAND_SUCCESS, - accounts, -}); - -export const expandDirectoryFail = error => ({ - type: DIRECTORY_EXPAND_FAIL, - error, -}); diff --git a/app/javascript/mastodon/actions/directory.ts b/app/javascript/mastodon/actions/directory.ts new file mode 100644 index 000000000..34ac309c6 --- /dev/null +++ b/app/javascript/mastodon/actions/directory.ts @@ -0,0 +1,37 @@ +import type { List as ImmutableList } from 'immutable'; + +import { apiGetDirectory } from 'mastodon/api/directory'; +import { createDataLoadingThunk } from 'mastodon/store/typed_functions'; + +import { fetchRelationships } from './accounts'; +import { importFetchedAccounts } from './importer'; + +export const fetchDirectory = createDataLoadingThunk( + 'directory/fetch', + async (params: Parameters[0]) => + apiGetDirectory(params), + (data, { dispatch }) => { + dispatch(importFetchedAccounts(data)); + dispatch(fetchRelationships(data.map((x) => x.id))); + + return { accounts: data }; + }, +); + +export const expandDirectory = createDataLoadingThunk( + 'directory/expand', + async (params: Parameters[0], { getState }) => { + const loadedItems = getState().user_lists.getIn([ + 'directory', + 'items', + ]) as ImmutableList; + + return apiGetDirectory({ ...params, offset: loadedItems.size }, 20); + }, + (data, { dispatch }) => { + dispatch(importFetchedAccounts(data)); + dispatch(fetchRelationships(data.map((x) => x.id))); + + return { accounts: data }; + }, +); diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index d906bdfb1..516a7a797 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -76,8 +76,8 @@ export function importFetchedStatuses(statuses) { pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id]))); } - if (status.card?.author_account) { - pushUnique(accounts, status.card.author_account); + if (status.card) { + status.card.authors.forEach(author => author.account && pushUnique(accounts, author.account)); } } diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index be76b0f39..c09a3f442 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -36,8 +36,15 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.poll = status.poll.id; } - if (status.card?.author_account) { - normalStatus.card = { ...status.card, author_account: status.card.author_account.id }; + if (status.card) { + normalStatus.card = { + ...status.card, + authors: status.card.authors.map(author => ({ + ...author, + accountId: author.account?.id, + account: undefined, + })), + }; } if (status.filtered) { diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 4ca3c3a15..f0ea46118 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -158,6 +158,7 @@ export const expandAccountTimeline = (accountId, { maxId, withReplies, t export const expandAccountFeaturedTimeline = (accountId, { tagged } = {}) => expandTimeline(`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true, tagged }); export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 }); export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done); +export const expandLinkTimeline = (url, { maxId } = {}, done = noOp) => expandTimeline(`link:${url}`, `/api/v1/timelines/link`, { url, max_id: maxId }, done); export const expandHashtagTimeline = (hashtag, { maxId, tags, local } = {}, done = noOp) => { return expandTimeline(`hashtag:${hashtag}${local ? ':local' : ''}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId, diff --git a/app/javascript/mastodon/actions/trends.js b/app/javascript/mastodon/actions/trends.js index 01089fccb..0bdf17a5d 100644 --- a/app/javascript/mastodon/actions/trends.js +++ b/app/javascript/mastodon/actions/trends.js @@ -51,7 +51,7 @@ export const fetchTrendingLinks = () => (dispatch) => { api() .get('/api/v1/trends/links', { params: { limit: 20 } }) .then(({ data }) => { - dispatch(importFetchedAccounts(data.map(link => link.author_account).filter(account => !!account))); + dispatch(importFetchedAccounts(data.flatMap(link => link.authors.map(author => author.account)).filter(account => !!account))); dispatch(fetchTrendingLinksSuccess(data)); }) .catch(err => dispatch(fetchTrendingLinksFail(err))); diff --git a/app/javascript/mastodon/api.ts b/app/javascript/mastodon/api.ts index e133125a2..24672290c 100644 --- a/app/javascript/mastodon/api.ts +++ b/app/javascript/mastodon/api.ts @@ -59,16 +59,49 @@ export default function api(withAuthorization = true) { }); } +type RequestParamsOrData = Record; + export async function apiRequest( method: Method, url: string, - params?: Record, + args: { + params?: RequestParamsOrData; + data?: RequestParamsOrData; + } = {}, ) { const { data } = await api().request({ method, url: '/api/' + url, - data: params, + ...args, }); return data; } + +export async function apiRequestGet( + url: string, + params?: RequestParamsOrData, +) { + return apiRequest('GET', url, { params }); +} + +export async function apiRequestPost( + url: string, + data?: RequestParamsOrData, +) { + return apiRequest('POST', url, { data }); +} + +export async function apiRequestPut( + url: string, + data?: RequestParamsOrData, +) { + return apiRequest('PUT', url, { data }); +} + +export async function apiRequestDelete( + url: string, + params?: RequestParamsOrData, +) { + return apiRequest('DELETE', url, { params }); +} diff --git a/app/javascript/mastodon/api/accounts.ts b/app/javascript/mastodon/api/accounts.ts index 3d89e44b2..bd1757e82 100644 --- a/app/javascript/mastodon/api/accounts.ts +++ b/app/javascript/mastodon/api/accounts.ts @@ -1,7 +1,7 @@ -import { apiRequest } from 'mastodon/api'; +import { apiRequestPost } from 'mastodon/api'; import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; export const apiSubmitAccountNote = (id: string, value: string) => - apiRequest('post', `v1/accounts/${id}/note`, { + apiRequestPost(`v1/accounts/${id}/note`, { comment: value, }); diff --git a/app/javascript/mastodon/api/directory.ts b/app/javascript/mastodon/api/directory.ts new file mode 100644 index 000000000..cd39f8f26 --- /dev/null +++ b/app/javascript/mastodon/api/directory.ts @@ -0,0 +1,15 @@ +import { apiRequestGet } from 'mastodon/api'; +import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; + +export const apiGetDirectory = ( + params: { + order: string; + local: boolean; + offset?: number; + }, + limit = 20, +) => + apiRequestGet('v1/directory', { + ...params, + limit, + }); diff --git a/app/javascript/mastodon/api/interactions.ts b/app/javascript/mastodon/api/interactions.ts index 4c466a1b4..118b5f06d 100644 --- a/app/javascript/mastodon/api/interactions.ts +++ b/app/javascript/mastodon/api/interactions.ts @@ -1,10 +1,10 @@ -import { apiRequest } from 'mastodon/api'; +import { apiRequestPost } from 'mastodon/api'; import type { Status, StatusVisibility } from 'mastodon/models/status'; export const apiReblog = (statusId: string, visibility: StatusVisibility) => - apiRequest<{ reblog: Status }>('post', `v1/statuses/${statusId}/reblog`, { + apiRequestPost<{ reblog: Status }>(`v1/statuses/${statusId}/reblog`, { visibility, }); export const apiUnreblog = (statusId: string) => - apiRequest('post', `v1/statuses/${statusId}/unreblog`); + apiRequestPost(`v1/statuses/${statusId}/unreblog`); diff --git a/app/javascript/mastodon/api/notification_policies.ts b/app/javascript/mastodon/api/notification_policies.ts index b2a1e5ac3..4032134fb 100644 --- a/app/javascript/mastodon/api/notification_policies.ts +++ b/app/javascript/mastodon/api/notification_policies.ts @@ -1,10 +1,9 @@ -import { apiRequest } from 'mastodon/api'; +import { apiRequestGet, apiRequestPut } from 'mastodon/api'; import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies'; export const apiGetNotificationPolicy = () => - apiRequest('GET', '/v1/notifications/policy'); + apiRequestGet('/v1/notifications/policy'); export const apiUpdateNotificationsPolicy = ( policy: Partial, -) => - apiRequest('PUT', '/v1/notifications/policy', policy); +) => apiRequestPut('/v1/notifications/policy', policy); diff --git a/app/javascript/mastodon/api_types/statuses.ts b/app/javascript/mastodon/api_types/statuses.ts index c7dd33b5d..a934faeb7 100644 --- a/app/javascript/mastodon/api_types/statuses.ts +++ b/app/javascript/mastodon/api_types/statuses.ts @@ -30,6 +30,12 @@ export interface ApiMentionJSON { acct: string; } +export interface ApiPreviewCardAuthorJSON { + name: string; + url: string; + account?: ApiAccountJSON; +} + export interface ApiPreviewCardJSON { url: string; title: string; @@ -38,6 +44,7 @@ export interface ApiPreviewCardJSON { type: string; author_name: string; author_url: string; + author_account?: ApiAccountJSON; provider_name: string; provider_url: string; html: string; @@ -48,6 +55,7 @@ export interface ApiPreviewCardJSON { embed_url: string; blurhash: string; published_at: string; + authors: ApiPreviewCardAuthorJSON[]; } export interface ApiStatusJSON { diff --git a/app/javascript/mastodon/components/account_bio.tsx b/app/javascript/mastodon/components/account_bio.tsx new file mode 100644 index 000000000..9d523c740 --- /dev/null +++ b/app/javascript/mastodon/components/account_bio.tsx @@ -0,0 +1,20 @@ +import { useLinks } from 'mastodon/../hooks/useLinks'; + +export const AccountBio: React.FC<{ + note: string; + className: string; +}> = ({ note, className }) => { + const handleClick = useLinks(); + + if (note.length === 0 || note === '

') { + return null; + } + + return ( +
+ ); +}; diff --git a/app/javascript/mastodon/components/account_fields.tsx b/app/javascript/mastodon/components/account_fields.tsx new file mode 100644 index 000000000..e297f99e3 --- /dev/null +++ b/app/javascript/mastodon/components/account_fields.tsx @@ -0,0 +1,42 @@ +import classNames from 'classnames'; + +import CheckIcon from '@/material-icons/400-24px/check.svg?react'; +import { useLinks } from 'mastodon/../hooks/useLinks'; +import { Icon } from 'mastodon/components/icon'; +import type { Account } from 'mastodon/models/account'; + +export const AccountFields: React.FC<{ + fields: Account['fields']; + limit: number; +}> = ({ fields, limit = -1 }) => { + const handleClick = useLinks(); + + if (fields.size === 0) { + return null; + } + + return ( +
+ {fields.take(limit).map((pair, i) => ( +
+
+ +
+ {pair.get('verified_at') && ( + + )} + +
+
+ ))} +
+ ); +}; diff --git a/app/javascript/mastodon/components/column_header.jsx b/app/javascript/mastodon/components/column_header.jsx deleted file mode 100644 index 42183f336..000000000 --- a/app/javascript/mastodon/components/column_header.jsx +++ /dev/null @@ -1,233 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent, useCallback } from 'react'; - -import { FormattedMessage, injectIntl, defineMessages, useIntl } from 'react-intl'; - -import classNames from 'classnames'; -import { withRouter } from 'react-router-dom'; - -import AddIcon from '@/material-icons/400-24px/add.svg?react'; -import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react'; -import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; -import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; -import CloseIcon from '@/material-icons/400-24px/close.svg?react'; -import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; -import { Icon } from 'mastodon/components/icon'; -import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context'; -import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { WithRouterPropTypes } from 'mastodon/utils/react_router'; - - -import { useAppHistory } from './router'; - -const messages = defineMessages({ - show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, - hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, - moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' }, - moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' }, - back: { id: 'column_back_button.label', defaultMessage: 'Back' }, -}); - -const BackButton = ({ onlyIcon }) => { - const history = useAppHistory(); - const intl = useIntl(); - - const handleBackClick = useCallback(() => { - if (history.location?.state?.fromMastodon) { - history.goBack(); - } else { - history.push('/'); - } - }, [history]); - - return ( - - ); -}; - -BackButton.propTypes = { - onlyIcon: PropTypes.bool, -}; - -class ColumnHeader extends PureComponent { - static propTypes = { - identity: identityContextPropShape, - intl: PropTypes.object.isRequired, - title: PropTypes.node, - icon: PropTypes.string, - iconComponent: PropTypes.func, - active: PropTypes.bool, - multiColumn: PropTypes.bool, - extraButton: PropTypes.node, - showBackButton: PropTypes.bool, - children: PropTypes.node, - pinned: PropTypes.bool, - placeholder: PropTypes.bool, - onPin: PropTypes.func, - onMove: PropTypes.func, - onClick: PropTypes.func, - appendContent: PropTypes.node, - collapseIssues: PropTypes.bool, - ...WithRouterPropTypes, - }; - - state = { - collapsed: true, - animating: false, - }; - - handleToggleClick = (e) => { - e.stopPropagation(); - this.setState({ collapsed: !this.state.collapsed, animating: true }); - }; - - handleTitleClick = () => { - this.props.onClick?.(); - }; - - handleMoveLeft = () => { - this.props.onMove(-1); - }; - - handleMoveRight = () => { - this.props.onMove(1); - }; - - handleTransitionEnd = () => { - this.setState({ animating: false }); - }; - - handlePin = () => { - if (!this.props.pinned) { - this.props.history.replace('/'); - } - - this.props.onPin(); - }; - - render () { - const { title, icon, iconComponent, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props; - const { collapsed, animating } = this.state; - - const wrapperClassName = classNames('column-header__wrapper', { - 'active': active, - }); - - const buttonClassName = classNames('column-header', { - 'active': active, - }); - - const collapsibleClassName = classNames('column-header__collapsible', { - 'collapsed': collapsed, - 'animating': animating, - }); - - const collapsibleButtonClassName = classNames('column-header__button', { - 'active': !collapsed, - }); - - let extraContent, pinButton, moveButtons, backButton, collapseButton; - - if (children) { - extraContent = ( -
- {children} -
- ); - } - - if (multiColumn && pinned) { - pinButton = ; - - moveButtons = ( -
- - -
- ); - } else if (multiColumn && this.props.onPin) { - pinButton = ; - } - - if (history && !pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) { - backButton = ; - } - - const collapsedContent = [ - extraContent, - ]; - - if (multiColumn) { - collapsedContent.push( -
- {pinButton} - {moveButtons} -
- ); - } - - if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) { - collapseButton = ( - - ); - } - - const hasTitle = (icon || iconComponent) && title; - - const component = ( -
-

- {hasTitle && ( - <> - {backButton} - - - - )} - - {!hasTitle && backButton} - -
- {extraButton} - {collapseButton} -
-

- -
-
- {(!collapsed || animating) && collapsedContent} -
-
- - {appendContent} -
- ); - - if (placeholder) { - return component; - } else { - return ( - {component} - ); - } - } - -} - -export default injectIntl(withIdentity(withRouter(ColumnHeader))); diff --git a/app/javascript/mastodon/components/column_header.tsx b/app/javascript/mastodon/components/column_header.tsx new file mode 100644 index 000000000..ec946cab3 --- /dev/null +++ b/app/javascript/mastodon/components/column_header.tsx @@ -0,0 +1,301 @@ +import { useCallback, useState } from 'react'; + +import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; + +import classNames from 'classnames'; + +import AddIcon from '@/material-icons/400-24px/add.svg?react'; +import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react'; +import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; +import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; +import CloseIcon from '@/material-icons/400-24px/close.svg?react'; +import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; +import type { IconProp } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; +import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context'; +import { useIdentity } from 'mastodon/identity_context'; + +import { useAppHistory } from './router'; + +const messages = defineMessages({ + show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, + hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, + moveLeft: { + id: 'column_header.moveLeft_settings', + defaultMessage: 'Move column to the left', + }, + moveRight: { + id: 'column_header.moveRight_settings', + defaultMessage: 'Move column to the right', + }, + back: { id: 'column_back_button.label', defaultMessage: 'Back' }, +}); + +const BackButton: React.FC<{ + onlyIcon: boolean; +}> = ({ onlyIcon }) => { + const history = useAppHistory(); + const intl = useIntl(); + + const handleBackClick = useCallback(() => { + if (history.location.state?.fromMastodon) { + history.goBack(); + } else { + history.push('/'); + } + }, [history]); + + return ( + + ); +}; + +export interface Props { + title?: string; + icon?: string; + iconComponent?: IconProp; + active?: boolean; + children?: React.ReactNode; + pinned?: boolean; + multiColumn?: boolean; + extraButton?: React.ReactNode; + showBackButton?: boolean; + placeholder?: boolean; + appendContent?: React.ReactNode; + collapseIssues?: boolean; + onClick?: () => void; + onMove?: (arg0: number) => void; + onPin?: () => void; +} + +export const ColumnHeader: React.FC = ({ + title, + icon, + iconComponent, + active, + children, + pinned, + multiColumn, + extraButton, + showBackButton, + placeholder, + appendContent, + collapseIssues, + onClick, + onMove, + onPin, +}) => { + const intl = useIntl(); + const { signedIn } = useIdentity(); + const history = useAppHistory(); + const [collapsed, setCollapsed] = useState(true); + const [animating, setAnimating] = useState(false); + + const handleToggleClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + setCollapsed((value) => !value); + setAnimating(true); + }, + [setCollapsed, setAnimating], + ); + + const handleTitleClick = useCallback(() => { + onClick?.(); + }, [onClick]); + + const handleMoveLeft = useCallback(() => { + onMove?.(-1); + }, [onMove]); + + const handleMoveRight = useCallback(() => { + onMove?.(1); + }, [onMove]); + + const handleTransitionEnd = useCallback(() => { + setAnimating(false); + }, [setAnimating]); + + const handlePin = useCallback(() => { + if (!pinned) { + history.replace('/'); + } + + onPin?.(); + }, [history, pinned, onPin]); + + const wrapperClassName = classNames('column-header__wrapper', { + active, + }); + + const buttonClassName = classNames('column-header', { + active, + }); + + const collapsibleClassName = classNames('column-header__collapsible', { + collapsed, + animating, + }); + + const collapsibleButtonClassName = classNames('column-header__button', { + active: !collapsed, + }); + + let extraContent, pinButton, moveButtons, backButton, collapseButton; + + if (children) { + extraContent = ( +
+ {children} +
+ ); + } + + if (multiColumn && pinned) { + pinButton = ( + + ); + + moveButtons = ( +
+ + +
+ ); + } else if (multiColumn && onPin) { + pinButton = ( + + ); + } + + if ( + !pinned && + ((multiColumn && history.location.state?.fromMastodon) || showBackButton) + ) { + backButton = ; + } + + const collapsedContent = [extraContent]; + + if (multiColumn) { + collapsedContent.push( +
+ {pinButton} + {moveButtons} +
, + ); + } + + if (signedIn && (children || (multiColumn && onPin))) { + collapseButton = ( + + ); + } + + const hasIcon = icon && iconComponent; + const hasTitle = hasIcon && title; + + const component = ( +
+

+ {hasTitle && ( + <> + {backButton} + + + + )} + + {!hasTitle && backButton} + +
+ {extraButton} + {collapseButton} +
+

+ +
+
+ {(!collapsed || animating) && collapsedContent} +
+
+ + {appendContent} +
+ ); + + if (placeholder) { + return component; + } else { + return {component}; + } +}; + +// eslint-disable-next-line import/no-default-export +export default ColumnHeader; diff --git a/app/javascript/mastodon/components/follow_button.tsx b/app/javascript/mastodon/components/follow_button.tsx new file mode 100644 index 000000000..ecc4e1ee1 --- /dev/null +++ b/app/javascript/mastodon/components/follow_button.tsx @@ -0,0 +1,128 @@ +import { useCallback, useEffect } from 'react'; + +import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; + +import { useIdentity } from '@/mastodon/identity_context'; +import { + fetchRelationships, + followAccount, + unfollowAccount, +} from 'mastodon/actions/accounts'; +import { openModal } from 'mastodon/actions/modal'; +import { Button } from 'mastodon/components/button'; +import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { me } from 'mastodon/initial_state'; +import { useAppDispatch, useAppSelector } from 'mastodon/store'; + +const messages = defineMessages({ + unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, + follow: { id: 'account.follow', defaultMessage: 'Follow' }, + followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' }, + mutual: { id: 'account.mutual', defaultMessage: 'Mutual' }, + edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, +}); + +export const FollowButton: React.FC<{ + accountId?: string; +}> = ({ accountId }) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const { signedIn } = useIdentity(); + const account = useAppSelector((state) => + accountId ? state.accounts.get(accountId) : undefined, + ); + const relationship = useAppSelector((state) => + accountId ? state.relationships.get(accountId) : undefined, + ); + const following = relationship?.following || relationship?.requested; + + useEffect(() => { + if (accountId && signedIn) { + dispatch(fetchRelationships([accountId])); + } + }, [dispatch, accountId, signedIn]); + + const handleClick = useCallback(() => { + if (!signedIn) { + dispatch( + openModal({ + modalType: 'INTERACTION', + modalProps: { + type: 'follow', + accountId: accountId, + url: account?.url, + }, + }), + ); + } + + if (!relationship) return; + + if (accountId === me) { + return; + } else if (relationship.following || relationship.requested) { + dispatch( + openModal({ + modalType: 'CONFIRM', + modalProps: { + message: ( + @{account?.acct} }} + /> + ), + confirm: intl.formatMessage(messages.unfollow), + onConfirm: () => { + dispatch(unfollowAccount(accountId)); + }, + }, + }), + ); + } else { + dispatch(followAccount(accountId)); + } + }, [dispatch, intl, accountId, relationship, account, signedIn]); + + let label; + + if (!signedIn) { + label = intl.formatMessage(messages.follow); + } else if (accountId === me) { + label = intl.formatMessage(messages.edit_profile); + } else if (!relationship) { + label = ; + } else if (relationship.following && relationship.followed_by) { + label = intl.formatMessage(messages.mutual); + } else if (!relationship.following && relationship.followed_by) { + label = intl.formatMessage(messages.followBack); + } else if (relationship.following || relationship.requested) { + label = intl.formatMessage(messages.unfollow); + } else { + label = intl.formatMessage(messages.follow); + } + + if (accountId === me) { + return ( + + {label} + + ); + } + + return ( + + ); +}; diff --git a/app/javascript/mastodon/components/hover_card_account.tsx b/app/javascript/mastodon/components/hover_card_account.tsx new file mode 100644 index 000000000..8933e14a9 --- /dev/null +++ b/app/javascript/mastodon/components/hover_card_account.tsx @@ -0,0 +1,74 @@ +import { useEffect, forwardRef } from 'react'; + +import classNames from 'classnames'; +import { Link } from 'react-router-dom'; + +import { fetchAccount } from 'mastodon/actions/accounts'; +import { AccountBio } from 'mastodon/components/account_bio'; +import { AccountFields } from 'mastodon/components/account_fields'; +import { Avatar } from 'mastodon/components/avatar'; +import { FollowersCounter } from 'mastodon/components/counters'; +import { DisplayName } from 'mastodon/components/display_name'; +import { FollowButton } from 'mastodon/components/follow_button'; +import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { ShortNumber } from 'mastodon/components/short_number'; +import { domain } from 'mastodon/initial_state'; +import { useAppSelector, useAppDispatch } from 'mastodon/store'; + +export const HoverCardAccount = forwardRef< + HTMLDivElement, + { accountId?: string } +>(({ accountId }, ref) => { + const dispatch = useAppDispatch(); + + const account = useAppSelector((state) => + accountId ? state.accounts.get(accountId) : undefined, + ); + + useEffect(() => { + if (accountId && !account) { + dispatch(fetchAccount(accountId)); + } + }, [dispatch, accountId, account]); + + return ( + + ); +}); + +HoverCardAccount.displayName = 'HoverCardAccount'; diff --git a/app/javascript/mastodon/components/hover_card_controller.tsx b/app/javascript/mastodon/components/hover_card_controller.tsx new file mode 100644 index 000000000..5ca55ebde --- /dev/null +++ b/app/javascript/mastodon/components/hover_card_controller.tsx @@ -0,0 +1,176 @@ +import { useEffect, useRef, useState, useCallback } from 'react'; + +import { useLocation } from 'react-router-dom'; + +import Overlay from 'react-overlays/Overlay'; +import type { + OffsetValue, + UsePopperOptions, +} from 'react-overlays/esm/usePopper'; + +import { useTimeout } from 'mastodon/../hooks/useTimeout'; +import { HoverCardAccount } from 'mastodon/components/hover_card_account'; + +const offset = [-12, 4] as OffsetValue; +const enterDelay = 750; +const leaveDelay = 150; +const popperConfig = { strategy: 'fixed' } as UsePopperOptions; + +const isHoverCardAnchor = (element: HTMLElement) => + element.matches('[data-hover-card-account]'); + +export const HoverCardController: React.FC = () => { + const [open, setOpen] = useState(false); + const [accountId, setAccountId] = useState(); + const [anchor, setAnchor] = useState(null); + const cardRef = useRef(null); + const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout(); + const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout(); + const [setScrollTimeout] = useTimeout(); + const location = useLocation(); + + const handleClose = useCallback(() => { + cancelEnterTimeout(); + cancelLeaveTimeout(); + setOpen(false); + setAnchor(null); + }, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]); + + useEffect(() => { + handleClose(); + }, [handleClose, location]); + + useEffect(() => { + let isScrolling = false; + let currentAnchor: HTMLElement | null = null; + + const open = (target: HTMLElement) => { + target.setAttribute('aria-describedby', 'hover-card'); + setOpen(true); + setAnchor(target); + setAccountId(target.getAttribute('data-hover-card-account') ?? undefined); + }; + + const close = () => { + currentAnchor?.removeAttribute('aria-describedby'); + currentAnchor = null; + setOpen(false); + setAnchor(null); + setAccountId(undefined); + }; + + const handleMouseEnter = (e: MouseEvent) => { + const { target } = e; + + // We've exited the window + if (!(target instanceof HTMLElement)) { + close(); + return; + } + + // We've entered an anchor + if (!isScrolling && isHoverCardAnchor(target)) { + cancelLeaveTimeout(); + + currentAnchor?.removeAttribute('aria-describedby'); + currentAnchor = target; + + setEnterTimeout(() => { + open(target); + }, enterDelay); + } + + // We've entered the hover card + if ( + !isScrolling && + (target === currentAnchor || target === cardRef.current) + ) { + cancelLeaveTimeout(); + } + }; + + const handleMouseLeave = (e: MouseEvent) => { + if (!currentAnchor) { + return; + } + + if (e.target === currentAnchor || e.target === cardRef.current) { + cancelEnterTimeout(); + + setLeaveTimeout(() => { + close(); + }, leaveDelay); + } + }; + + const handleScrollEnd = () => { + isScrolling = false; + }; + + const handleScroll = () => { + isScrolling = true; + cancelEnterTimeout(); + setScrollTimeout(handleScrollEnd, 100); + }; + + const handleMouseMove = () => { + delayEnterTimeout(enterDelay); + }; + + document.body.addEventListener('mouseenter', handleMouseEnter, { + passive: true, + capture: true, + }); + + document.body.addEventListener('mousemove', handleMouseMove, { + passive: true, + capture: false, + }); + + document.body.addEventListener('mouseleave', handleMouseLeave, { + passive: true, + capture: true, + }); + + document.addEventListener('scroll', handleScroll, { + passive: true, + capture: true, + }); + + return () => { + document.body.removeEventListener('mouseenter', handleMouseEnter); + document.body.removeEventListener('mousemove', handleMouseMove); + document.body.removeEventListener('mouseleave', handleMouseLeave); + document.removeEventListener('scroll', handleScroll); + }; + }, [ + setEnterTimeout, + setLeaveTimeout, + setScrollTimeout, + cancelEnterTimeout, + cancelLeaveTimeout, + delayEnterTimeout, + setOpen, + setAccountId, + setAnchor, + ]); + + return ( + + {({ props }) => ( +
+ +
+ )} +
+ ); +}; diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 7b97e4576..dce48d703 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -425,7 +425,7 @@ class Status extends ImmutablePureComponent { prepend = (
- }} /> + }} />
); @@ -446,7 +446,7 @@ class Status extends ImmutablePureComponent { prepend = (
- }} /> + }} />
); } @@ -562,7 +562,7 @@ class Status extends ImmutablePureComponent { {status.get('edited_at') && *} - +
{statusAvatar}
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx index 24483cf51..82135b85c 100644 --- a/app/javascript/mastodon/components/status_content.jsx +++ b/app/javascript/mastodon/components/status_content.jsx @@ -116,8 +116,9 @@ class StatusContent extends PureComponent { if (mention) { link.addEventListener('click', this.onMentionClick.bind(this, mention), false); - link.setAttribute('title', `@${mention.get('acct')}`); + link.removeAttribute('title'); link.setAttribute('href', `/@${mention.get('acct')}`); + link.setAttribute('data-hover-card-account', mention.get('id')); } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) { link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false); link.setAttribute('href', `/tags/${link.text.replace(/^#/, '')}`); diff --git a/app/javascript/mastodon/components/status_list.jsx b/app/javascript/mastodon/components/status_list.jsx index 3ed20f65e..fee6675fa 100644 --- a/app/javascript/mastodon/components/status_list.jsx +++ b/app/javascript/mastodon/components/status_list.jsx @@ -33,6 +33,7 @@ export default class StatusList extends ImmutablePureComponent { withCounters: PropTypes.bool, timelineId: PropTypes.string, lastId: PropTypes.string, + bindToDocument: PropTypes.bool, }; static defaultProps = { diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index b10ef6ef7..1326874e5 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -94,7 +94,7 @@ const messageForFollowButton = relationship => { return messages.mutual; } else if (!relationship.get('following') && relationship.get('followed_by')) { return messages.followBack; - } else if (relationship.get('following')) { + } else if (relationship.get('following') || relationship.get('requested')) { return messages.unfollow; } else { return messages.follow; @@ -291,10 +291,8 @@ class Header extends ImmutablePureComponent { if (me !== account.get('id')) { if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded actionBtn = ; - } else if (account.getIn(['relationship', 'requested'])) { - actionBtn =
diff --git a/app/javascript/mastodon/features/explore/components/story.jsx b/app/javascript/mastodon/features/explore/components/story.jsx index a2cae942d..125df412a 100644 --- a/app/javascript/mastodon/features/explore/components/story.jsx +++ b/app/javascript/mastodon/features/explore/components/story.jsx @@ -4,6 +4,8 @@ import { useState, useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; +import { Link } from 'react-router-dom'; + import { Blurhash } from 'mastodon/components/blurhash'; @@ -57,7 +59,7 @@ export const Story = ({
{author ? : {author} }} /> : } - {typeof sharedTimes === 'number' ? : } + {typeof sharedTimes === 'number' ? : }
diff --git a/app/javascript/mastodon/features/explore/links.jsx b/app/javascript/mastodon/features/explore/links.jsx index 93fd1fb6d..035e5aaad 100644 --- a/app/javascript/mastodon/features/explore/links.jsx +++ b/app/javascript/mastodon/features/explore/links.jsx @@ -75,7 +75,7 @@ class Links extends PureComponent { publisher={link.get('provider_name')} publishedAt={link.get('published_at')} author={link.get('author_name')} - authorAccount={link.getIn(['author_account', 'id'])} + authorAccount={link.getIn(['authors', 0, 'account', 'id'])} sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1} thumbnail={link.get('image')} thumbnailDescription={link.get('image_description')} diff --git a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx index c39b43bad..1b8040e55 100644 --- a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx +++ b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx @@ -12,12 +12,11 @@ import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import InfoIcon from '@/material-icons/400-24px/info.svg?react'; -import { followAccount, unfollowAccount } from 'mastodon/actions/accounts'; import { changeSetting } from 'mastodon/actions/settings'; import { fetchSuggestions, dismissSuggestion } from 'mastodon/actions/suggestions'; import { Avatar } from 'mastodon/components/avatar'; -import { Button } from 'mastodon/components/button'; import { DisplayName } from 'mastodon/components/display_name'; +import { FollowButton } from 'mastodon/components/follow_button'; import { Icon } from 'mastodon/components/icon'; import { IconButton } from 'mastodon/components/icon_button'; import { VerifiedBadge } from 'mastodon/components/verified_badge'; @@ -79,18 +78,8 @@ Source.propTypes = { const Card = ({ id, sources }) => { const intl = useIntl(); const account = useSelector(state => state.getIn(['accounts', id])); - const relationship = useSelector(state => state.getIn(['relationships', id])); const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); const dispatch = useDispatch(); - const following = relationship?.get('following') ?? relationship?.get('requested'); - - const handleFollow = useCallback(() => { - if (following) { - dispatch(unfollowAccount(id)); - } else { - dispatch(followAccount(id)); - } - }, [id, following, dispatch]); const handleDismiss = useCallback(() => { dispatch(dismissSuggestion(id)); @@ -109,7 +98,7 @@ const Card = ({ id, sources }) => { {firstVerifiedField ? : } -
.", "domain_pill.who_you_are": "Como o teu alcume informa de quen es e onde estás, as persoas poden interactuar contigo desde toda a web social de .", "domain_pill.your_handle": "O teu alcume:", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 1c50ba8e1..8111a56e8 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -35,9 +35,9 @@ "account.follow_back": "לעקוב בחזרה", "account.followers": "עוקבי×", "account.followers.empty": "××£ ×חד ×œ× ×¢×•×§×‘ ×חר המשתמש ×”×–×” עדיין.", - "account.followers_counter": "{count, plural,one {עוקב ×חד} other {{counter} עוקבי×}}", + "account.followers_counter": "{count, plural,one {עוקב ×חד} other {{count} עוקבי×}}", "account.following": "נעקבי×", - "account.following_counter": "{count, plural,one {עוקב ×חרי {counter}}other {עוקב ×חרי {counter}}}", + "account.following_counter": "{count, plural,one {עוקב ×חרי {count}}other {עוקב ×חרי {count}}}", "account.follows.empty": "משתמש ×–×” עדיין ×œ× ×¢×•×§×‘ ×חרי ××£ ×חד.", "account.go_to_profile": "מעבר לפרופיל", "account.hide_reblogs": "להסתיר ×”×™×“×”×•×“×™× ×ž×ת @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} ביקשו לעקוב ×חריך", "account.share": "שתף ×ת הפרופיל של @{name}", "account.show_reblogs": "הצג ×”×“×”×•×“×™× ×ž×ת @{name}", - "account.statuses_counter": "{count, plural, one {הודעה} two {הודעותיי×} many {{count} הודעות} other {{count} הודעות}}", + "account.statuses_counter": "{count, plural, one {הודעה ×חת} two {הודעותיי×} many {{count} הודעות} other {{count} הודעות}}", "account.unblock": "להסיר חסימה ל- @{name}", "account.unblock_domain": "הסירי ×ת החסימה של קהילת {domain}", "account.unblock_short": "הסר חסימה", @@ -696,8 +696,11 @@ "server_banner.about_active_users": "×ž×©×ª×ž×©×™× ×¤×¢×™×œ×™× ×‘×©×¨×ª ב־30 ×”×™×ž×™× ×”××—×¨×•× ×™× (×ž×©×ª×ž×©×™× ×¤×¢×™×œ×™× ×—×•×“×©×™×™×)", "server_banner.active_users": "×ž×©×ª×ž×©×™× ×¤×¢×™×œ×™×", "server_banner.administered_by": "מנוהל ×¢\"×™:", + "server_banner.is_one_of_many": "{domain} ×”×•× ×©×¨×ª ×חד משרתי מסטודון עצמ××™×™× ×¨×‘×™× ×©×“×¨×’× ×ª×•×›×œ×• להשתתף בפדיוורס (רשת חברתית מבוזרת).", "server_banner.server_stats": "סטטיסטיקות שרת:", "sign_in_banner.create_account": "יצירת חשבון", + "sign_in_banner.follow_anyone": "תוכלו לעקוב ×חרי כל משמתמש בפדיוורס ×•×œ×§×¨×•× ×”×›×œ לפי סדר ×”×¤×¨×¡×•× ×‘×¦×™×¨ הזמן. ×ין ×לגוריתמי×, פרסומות, ×ו קליקבייט ×ž×˜×¢× ×‘×¢×œ×™ הרשת.", + "sign_in_banner.mastodon_is": "מסטודון ×”×•× ×”×“×¨×š הטובה ביותר לעקוב ×חרי מה שקורה.", "sign_in_banner.sign_in": "התחברות", "sign_in_banner.sso_redirect": "התחברות/הרשמה", "status.admin_account": "פתח/×™ ממשק ניהול עבור @{name}", @@ -774,7 +777,7 @@ "timeline_hint.resources.followers": "עוקבי×", "timeline_hint.resources.follows": "נעקבי×", "timeline_hint.resources.statuses": "הודעות ישנות יותר", - "trends.counter_by_accounts": "{count, plural, one {××“× {count}} other {{count} ×.נשי×}} {days, plural, one {מ××– ×תמול} two {×‘×™×•×ž×™×™× ×”×חרוני×} other {במשך {days} ×”×™×ž×™× ×”×חרוני×}}", + "trends.counter_by_accounts": "{count, plural, one {××“× ×חד} other {{count} ×.נשי×}} {days, plural, one {מ××– ×תמול} two {×‘×™×•×ž×™×™× ×”×חרוני×} other {במשך {days} ×”×™×ž×™× ×”×חרוני×}}", "trends.trending_now": "נוש××™× ×—×ž×™×", "ui.beforeunload": "×”×˜×™×•×˜× ×ª×בד ×× ×ª×¢×–×‘×• ×ת מסטודון.", "units.short.billion": "{count} מלי×רד", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index a2da55da8..89c71207f 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -35,9 +35,7 @@ "account.follow_back": "फॉलो करें", "account.followers": "फॉलोवर", "account.followers.empty": "कोई भी इस यूज़रॠको फ़ॉलो नहीं करता है", - "account.followers_counter": "{count, plural, one {{counter} अनà¥à¤—ामी} other {{counter} समरà¥à¤¥à¤•}}", "account.following": "फॉलोइंग", - "account.following_counter": "{count, plural, one {{counter} निमà¥à¤¨à¤²à¤¿à¤–ित} other {{counter} निमà¥à¤¨à¤²à¤¿à¤–ित}}", "account.follows.empty": "यह यूज़रॠअभी तक किसी को फॉलो नहीं करता है।", "account.go_to_profile": "पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² में जाà¤à¤", "account.hide_reblogs": "@{name} के बूसà¥à¤Ÿ छà¥à¤ªà¤¾à¤à¤‚", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} ने आपको फॉलो करने के लिठअनà¥à¤°à¥‹à¤§ किया है", "account.share": "@{name} की पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² शेयर करे", "account.show_reblogs": "@{name} के बूसà¥à¤Ÿ दिखाà¤", - "account.statuses_counter": "{count, plural, one {{counter} भोंपू} other {{counter} भोंपू}}", "account.unblock": "@{name} को अनबà¥à¤²à¥‰à¤• करें", "account.unblock_domain": "{domain} दिखाà¤", "account.unblock_short": "अनबà¥à¤²à¥‰à¤•", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index d952945c4..c8f6f0186 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -35,9 +35,7 @@ "account.follow_back": "Slijedi natrag", "account.followers": "Pratitelji", "account.followers.empty": "Nitko joÅ¡ ne prati korisnika/cu.", - "account.followers_counter": "{count, plural, one {{counter} pratitelj} other {{counter} pratitelja}}", "account.following": "Pratim", - "account.following_counter": "{count, plural, one {{counter} praćeni} few{{counter} praćena} other {{counter} praćenih}}", "account.follows.empty": "Korisnik/ca joÅ¡ ne prati nikoga.", "account.go_to_profile": "Idi na profil", "account.hide_reblogs": "Sakrij boostove od @{name}", @@ -62,7 +60,6 @@ "account.requested_follow": "{name} zatražio/la je praćenje", "account.share": "Podijeli profil @{name}", "account.show_reblogs": "Prikaži boostove od @{name}", - "account.statuses_counter": "{count, plural, one {{counter} toot} other {{counter} toota}}", "account.unblock": "Deblokiraj @{name}", "account.unblock_domain": "Deblokiraj domenu {domain}", "account.unblock_short": "Deblokiraj", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 6164335da..1fcadc8f9 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -35,9 +35,9 @@ "account.follow_back": "Viszontkövetés", "account.followers": "KövetÅ‘", "account.followers.empty": "Ezt a felhasználót még senki sem követi.", - "account.followers_counter": "{count, plural, one {{counter} KövetÅ‘} other {{counter} KövetÅ‘}}", + "account.followers_counter": "{count, plural, one {{counter} követÅ‘} other {{counter} követÅ‘}}", "account.following": "Követve", - "account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}", + "account.following_counter": "{count, plural, one {{counter} követett} other {{counter} követett}}", "account.follows.empty": "Ez a felhasználó még senkit sem követ.", "account.go_to_profile": "Ugrás a profilhoz", "account.hide_reblogs": "@{name} megtolásainak elrejtése", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} kérte, hogy követhessen", "account.share": "@{name} profiljának megosztása", "account.show_reblogs": "@{name} megtolásainak mutatása", - "account.statuses_counter": "{count, plural, one {{counter} Bejegyzés} other {{counter} Bejegyzés}}", + "account.statuses_counter": "{count, plural, one {{counter} bejegyzés} other {{counter} bejegyzés}}", "account.unblock": "@{name} letiltásának feloldása", "account.unblock_domain": "{domain} domain tiltásának feloldása", "account.unblock_short": "Tiltás feloldása", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index cd29f441d..b4abe9bf0 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -28,9 +28,7 @@ "account.follow": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬", "account.followers": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Õ²Õ¶Õ¥Ö€", "account.followers.empty": "Ô±ÕµÕ½ Ö…Õ£Õ¿Õ¡Õ¿Õ«Ö€Õ¸Õ»Õ¨ Õ¤Õ¥Õ¼ Õ¸Õ¹ Õ´Õ§Õ¯ Õ¹Õ« Õ°Õ¥Õ¿Õ¥Ö‚Õ¸Ö‚Õ´Ö‰", - "account.followers_counter": "{count, plural, one {{counter} Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Ö€Õ¤} other {{counter} Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Ö€Õ¤}}", "account.following": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¡Õ®", - "account.following_counter": "{count, plural, one {{counter} Õ€Õ¥Õ¿Õ¥Ö‚Õ¡Õ®} other {{counter} Õ€Õ¥Õ¿Õ¥Ö‚Õ¡Õ®}}", "account.follows.empty": "Ô±ÕµÕ½ Ö…Õ£Õ¿Õ¡Õ¿Õ§Ö€Õ¨ Õ¤Õ¥Õ¼ Õ¸Õ¹ Õ´Õ§Õ¯Õ« Õ¹Õ« Õ°Õ¥Õ¿Õ¥Ö‚Õ¸Ö‚Õ´Ö‰", "account.go_to_profile": "Ô³Õ¶Õ¡Õ¬ Õ¡Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶ Õ°Õ¡Õ·Õ«Ö‚", "account.hide_reblogs": "Ô¹Õ¡Ö„ÖÕ¶Õ¥Õ¬ @{name}ÖŠÕ« Õ¿Õ¡Ö€Õ¡Õ®Õ¡Õ®Õ¶Õ¥Ö€Õ¨", @@ -52,7 +50,6 @@ "account.requested_follow": "{name}-Õ¨ ÖÕ¡Õ¶Õ¯Õ¡Õ¶Õ¸Ö‚Õ´ Õ§ Õ°Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬ Ö„Õ¥Õ¦", "account.share": "Ô¿Õ«Õ½Õ¸Ö‚Õ¥Õ¬ @{name}ÖŠÕ« Õ§Õ»Õ¸Õ¾", "account.show_reblogs": "Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ @{name}ÖŠÕ« Õ¿Õ¡Ö€Õ¡Õ®Õ¡Õ®Õ¶Õ¥Ö€Õ¨", - "account.statuses_counter": "{count, plural, one {{counter} Ô³Ö€Õ¡Õ¼Õ¸Ö‚Õ´} other {{counter} Ô³Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€}}", "account.unblock": "Ô±ÕºÕ¡Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¥Õ¬ @{name}ÖŠÕ«Õ¶", "account.unblock_domain": "Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ {domain} Õ©Õ¡Ö„ÖÕ¸Ö‚Õ¡Õ® Õ¿Õ«Ö€Õ¸ÕµÕ©Õ« Õ£Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨", "account.unblock_short": "Ô±Ö€Õ£Õ¥Õ¬Õ¡Õ¢Õ¡ÖÕ¥Õ¬", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index 53cc93859..ace6402ee 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -354,7 +354,7 @@ "home.pending_critical_update.link": "Vider actualisationes", "home.pending_critical_update.title": "Actualisation de securitate critic disponibile!", "home.show_announcements": "Monstrar annuncios", - "interaction_modal.description.favourite": "Con un conto sur Mastodon, tu pote marcar iste message como favorite pro informar le autor que tu lo apprecia e salveguarda pro plus tarde.", + "interaction_modal.description.favourite": "Con un conto sur Mastodon, tu pote marcar iste message como favorite pro informar le autor que tu lo apprecia e lo salva pro plus tarde.", "interaction_modal.description.follow": "Con un conto sur Mastodon, tu pote sequer {name} e reciper su messages in tu fluxo de initio.", "interaction_modal.description.reblog": "Con un conto sur Mastodon, tu pote impulsar iste message pro condivider lo con tu proprie sequitores.", "interaction_modal.description.reply": "Con un conto sur Mastodon, tu pote responder a iste message.", @@ -699,6 +699,7 @@ "server_banner.is_one_of_many": "{domain} es un de multe servitores independente de Mastodon que tu pote usar pro participar in le fediverso.", "server_banner.server_stats": "Statos del servitor:", "sign_in_banner.create_account": "Crear un conto", + "sign_in_banner.follow_anyone": "Seque quicunque in le fediverso, e tu videra toto in ordine chronologic. Sin algorithmo, sin publicitate, sin titulos de esca.", "sign_in_banner.mastodon_is": "Mastodon es le melior maniera de sequer lo que passa.", "sign_in_banner.sign_in": "Aperir session", "sign_in_banner.sso_redirect": "Aperir session o crear conto", @@ -763,7 +764,7 @@ "status.unmute_conversation": "Non plus silentiar conversation", "status.unpin": "Disfixar del profilo", "subscribed_languages.lead": "Solmente le messages in le linguas seligite apparera in tu chronologias de initio e de listas post le cambiamento. Selige necun pro reciper messages in tote le linguas.", - "subscribed_languages.save": "Salveguardar le cambiamentos", + "subscribed_languages.save": "Salvar le cambiamentos", "subscribed_languages.target": "Cambiar le linguas subscribite pro {target}", "tabs_bar.home": "Initio", "tabs_bar.notifications": "Notificationes", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index d86b5854f..f4e5e1bae 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -35,9 +35,7 @@ "account.follow_back": "Ikuti balik", "account.followers": "Pengikut", "account.followers.empty": "Pengguna ini belum ada pengikut.", - "account.followers_counter": "{count, plural, other {{counter} Pengikut}}", "account.following": "Mengikuti", - "account.following_counter": "{count, plural, other {{counter} Mengikuti}}", "account.follows.empty": "Pengguna ini belum mengikuti siapa pun.", "account.go_to_profile": "Buka profil", "account.hide_reblogs": "Sembunyikan boosts dari @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} ingin mengikuti Anda", "account.share": "Bagikan profil @{name}", "account.show_reblogs": "Tampilkan boost dari @{name}", - "account.statuses_counter": "{count, plural, other {{counter} Kiriman}}", "account.unblock": "Buka blokir @{name}", "account.unblock_domain": "Buka blokir domain {domain}", "account.unblock_short": "Buka blokir", diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json index f15b98288..c75788c43 100644 --- a/app/javascript/mastodon/locales/ie.json +++ b/app/javascript/mastodon/locales/ie.json @@ -35,9 +35,7 @@ "account.follow_back": "Sequer reciprocmen", "account.followers": "Sequitores", "account.followers.empty": "Ancor nequi seque ti-ci usator.", - "account.followers_counter": "{count, plural, one {{counter} Sequitor} other {{counter} Sequitor}}", "account.following": "Sequent", - "account.following_counter": "{count, plural, one {{counter} Sequent} other {{counter} Sequent}}", "account.follows.empty": "Ti-ci usator ancor ne seque quemcunc.", "account.go_to_profile": "Ear a profil", "account.hide_reblogs": "Celar boosts de @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} ha petit sequer te", "account.share": "Distribuer li profil de @{name}", "account.show_reblogs": "Monstrar boosts de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Posta} other {{counter} Postas}}", "account.unblock": "Desbloccar @{name}", "account.unblock_domain": "Desbloccar dominia {domain}", "account.unblock_short": "Desbloccar", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 016a111c4..6aa954ae5 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -33,9 +33,7 @@ "account.follow": "Sequar", "account.followers": "Sequanti", "account.followers.empty": "Nulu sequas ca uzanto til nun.", - "account.followers_counter": "{count, plural, one {{counter} Sequanto} other {{counter} Sequanti}}", "account.following": "Sequata", - "account.following_counter": "{count, plural, one {{counter} Sequas} other {{counter} Sequanti}}", "account.follows.empty": "Ca uzanto ne sequa irgu til nun.", "account.go_to_profile": "Irez al profilo", "account.hide_reblogs": "Celez repeti de @{name}", @@ -60,7 +58,6 @@ "account.requested_follow": "{name} demandis sequar tu", "account.share": "Partigez profilo di @{name}", "account.show_reblogs": "Montrez repeti de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Posto} other {{counter} Posti}}", "account.unblock": "Desblokusar @{name}", "account.unblock_domain": "Desblokusar {domain}", "account.unblock_short": "Desblokusar", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 08605f523..1a38591b8 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -63,7 +63,7 @@ "account.requested_follow": "{name} hefur beðið um að fylgjast með þér", "account.share": "Deila notandasniði fyrir @{name}", "account.show_reblogs": "Sýna endurbirtingar frá @{name}", - "account.statuses_counter": "{count, plural, one {Færsla: {counter}} other {Færslur: {counter}}}", + "account.statuses_counter": "{count, plural, one {{counter} færsla} other {{counter} færslur}}", "account.unblock": "Aflétta útilokun af @{name}", "account.unblock_domain": "Aflétta útilokun lénsins {domain}", "account.unblock_short": "Hætta að loka á", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 3672b5fd7..73c4f9ba6 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -35,9 +35,9 @@ "account.follow_back": "Segui a tua volta", "account.followers": "Follower", "account.followers.empty": "Ancora nessuno segue questo utente.", - "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}", + "account.followers_counter": "{count, plural, one {{counter} seguace} other {{counter} seguaci}}", "account.following": "Seguiti", - "account.following_counter": "{count, plural, one {{counter} Seguiti} other {{counter} Seguiti}}", + "account.following_counter": "{count, plural, one {{counter} segui} other {{counter} segui}}", "account.follows.empty": "Questo utente non segue ancora nessuno.", "account.go_to_profile": "Vai al profilo", "account.hide_reblogs": "Nascondi potenziamenti da @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} ha richiesto di seguirti", "account.share": "Condividi il profilo di @{name}", "account.show_reblogs": "Mostra potenziamenti da @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Post}}", + "account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} post}}", "account.unblock": "Sblocca @{name}", "account.unblock_domain": "Sblocca il dominio {domain}", "account.unblock_short": "Sblocca", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 90a46edd5..60788baff 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -35,9 +35,7 @@ "account.follow_back": "フォローãƒãƒƒã‚¯", "account.followers": "フォロワー", "account.followers.empty": "ã¾ã èª°ã‚‚フォローã—ã¦ã„ã¾ã›ã‚“。", - "account.followers_counter": "{counter} フォロワー", "account.following": "フォロー中", - "account.following_counter": "{counter} フォロー", "account.follows.empty": "ã¾ã èª°ã‚‚フォローã—ã¦ã„ã¾ã›ã‚“。", "account.go_to_profile": "プロフィールページã¸", "account.hide_reblogs": "@{name}ã•ã‚“ã‹ã‚‰ã®ãƒ–ーストをéžè¡¨ç¤º", @@ -63,7 +61,6 @@ "account.requested_follow": "{name}ã•ã‚“ãŒã‚ãªãŸã«ãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã—ã¾ã—ãŸ", "account.share": "@{name}ã•ã‚“ã®ãƒ—ロフィールを共有ã™ã‚‹", "account.show_reblogs": "@{name}ã•ã‚“ã‹ã‚‰ã®ãƒ–ーストを表示", - "account.statuses_counter": "{counter} 投稿", "account.unblock": "@{name}ã•ã‚“ã®ãƒ–ロックを解除", "account.unblock_domain": "{domain}ã®ãƒ–ロックを解除", "account.unblock_short": "ブロック解除", @@ -414,6 +411,7 @@ "limited_account_hint.action": "構ã‚ãšè¡¨ç¤ºã™ã‚‹", "limited_account_hint.title": "ã“ã®ãƒ—ロフィールã¯{domain}ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã«ã‚ˆã£ã¦éžè¡¨ç¤ºã«ã•ã‚Œã¦ã„ã¾ã™ã€‚", "link_preview.author": "{name}", + "link_preview.more_from_author": "{name}ã•ã‚“ã®æŠ•ç¨¿ã‚’ã‚‚ã£ã¨èª­ã‚€", "lists.account.add": "リストã«è¿½åŠ ", "lists.account.remove": "リストã‹ã‚‰å¤–ã™", "lists.delete": "リストを削除", @@ -696,6 +694,7 @@ "server_banner.administered_by": "管ç†è€…", "server_banner.server_stats": "サーãƒãƒ¼ã®æƒ…å ±", "sign_in_banner.create_account": "アカウント作æˆ", + "sign_in_banner.follow_anyone": "連åˆå†…ã®èª°ã§ã‚‚フォローã—ã¦æŠ•ç¨¿ã‚’時系列ã§è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚アルゴリズムã€åºƒå‘Šã€ã‚¯ãƒªãƒƒã‚¯ãƒ™ã‚¤ãƒˆã¯ã‚ã‚Šã¾ã›ã‚“。", "sign_in_banner.sign_in": "ログイン", "sign_in_banner.sso_redirect": "ログインã¾ãŸã¯ç™»éŒ²", "status.admin_account": "@{name}ã•ã‚“ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ç”»é¢ã‚’é–‹ã", diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json index 7af4dccd8..b2e67e143 100644 --- a/app/javascript/mastodon/locales/ka.json +++ b/app/javascript/mastodon/locales/ka.json @@ -26,7 +26,6 @@ "account.requested": "დáƒáƒ›áƒ¢áƒ™áƒ˜áƒªáƒ”ბის მáƒáƒšáƒáƒ“ინში. დáƒáƒáƒ¬áƒ™áƒáƒžáƒ£áƒœáƒ”თ რáƒáƒ› უáƒáƒ áƒ§áƒáƒ— დáƒáƒ“ევნების მáƒáƒ—ხáƒáƒœáƒ•áƒ", "account.share": "გáƒáƒáƒ–იáƒáƒ áƒ” @{name}-ის პრáƒáƒ¤áƒ˜áƒšáƒ˜", "account.show_reblogs": "áƒáƒ©áƒ•áƒ”ნე ბუსტები @{name}-სგáƒáƒœ", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "გáƒáƒœáƒ‘ლáƒáƒ™áƒ” @{name}", "account.unblock_domain": "გáƒáƒ›áƒáƒáƒ©áƒ˜áƒœáƒ” {domain}", "account.unendorse": "áƒáƒ  გáƒáƒ›áƒáƒ˜áƒ áƒ©áƒ”ს პრáƒáƒ¤áƒ˜áƒšáƒ–ე", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 5aa46bafd..de866cc1b 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -26,9 +26,7 @@ "account.follow": "Ḍfer", "account.followers": "Imeá¸faren", "account.followers.empty": "Ar tura, ulac yiwen i yeá¹­á¹­afaá¹›en amseqdac-agi.", - "account.followers_counter": "{count, plural, one {{count} n umeá¸far} other {{count} n imeá¸faren}}", "account.following": "Yeá¹­á¹­afaá¹›", - "account.following_counter": "{count, plural, one {{counter} yettwaá¸faren} other {{counter} yettwaá¸faren}}", "account.follows.empty": "Ar tura, amseqdac-agi ur yeá¹­á¹­afaá¹› yiwen.", "account.go_to_profile": "Ddu É£er umaÉ£nu", "account.hide_reblogs": "Ffer ayen i ibeá¹­á¹­u @{name}", @@ -51,7 +49,6 @@ "account.requested_follow": "{name} yessuter ad k·m-yeá¸fer", "account.share": "Bá¸u amaÉ£nu n @{name}", "account.show_reblogs": "Ssken-d inebá¸a n @{name}", - "account.statuses_counter": "{count, plural, one {{counter} n tsuffeÉ£t} other {{counter} n tsuffaÉ£}}", "account.unblock": "Serreḥ i @{name}", "account.unblock_domain": "Ssken-d {domain}", "account.unblock_short": "Serreḥ", diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json index bd0a806cd..efeee16c6 100644 --- a/app/javascript/mastodon/locales/kk.json +++ b/app/javascript/mastodon/locales/kk.json @@ -31,9 +31,7 @@ "account.follow": "Жазылу", "account.followers": "Жазылушы", "account.followers.empty": "Бұл қолданушыға әлі ешкім жазылмаған.", - "account.followers_counter": "{count, plural, one {{counter} жазылушы} other {{counter} жазылушы}}", "account.following": "Жазылым", - "account.following_counter": "{count, plural, one {{counter} жазылым} other {{counter} жазылым}}", "account.follows.empty": "Бұл қолданушы әлі ешкімге жазылмаған.", "account.go_to_profile": "Профиліне өту", "account.hide_reblogs": "@{name} буÑтарын жаÑыру", @@ -52,7 +50,6 @@ "account.requested": "РаÑтауын күтіңіз. Жазылудан Ð±Ð°Ñ Ñ‚Ð°Ñ€Ñ‚Ñƒ үшін баÑыңыз", "account.share": "@{name} профилін бөліÑу\"", "account.show_reblogs": "@{name} бөліÑкендерін көрÑету", - "account.statuses_counter": "{count, plural, one {{counter} ПоÑÑ‚} other {{counter} ПоÑÑ‚}}", "account.unblock": "Бұғаттан шығару @{name}", "account.unblock_domain": "Бұғаттан шығару {domain}", "account.unendorse": "Профильде рекомендемеу", diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json index ceb0f8b9b..24592e37f 100644 --- a/app/javascript/mastodon/locales/kn.json +++ b/app/javascript/mastodon/locales/kn.json @@ -16,7 +16,6 @@ "account.posts": "ಟೂಟà³â€Œà²—ಳà³", "account.posts_with_replies": "Toots and replies", "account.requested": "Awaiting approval", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock_domain": "Unhide {domain}", "account_note.placeholder": "Click to add a note", "alert.unexpected.title": "ಅಯà³à²¯à³‹!", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index c4c084d98..fe3582c1d 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -35,9 +35,9 @@ "account.follow_back": "맞팔로우 하기", "account.followers": "팔로워", "account.followers.empty": "ì•„ì§ ì•„ë¬´ë„ ì´ ì‚¬ìš©ìžë¥¼ 팔로우하고 있지 않습니다.", - "account.followers_counter": "{counter} 팔로워", + "account.followers_counter": "{count, plural, other {{counter} 팔로워}}", "account.following": "팔로잉", - "account.following_counter": "{counter} 팔로잉", + "account.following_counter": "{count, plural, other {{counter} 팔로잉}}", "account.follows.empty": "ì´ ì‚¬ìš©ìžëŠ” ì•„ì§ ì•„ë¬´ë„ íŒ”ë¡œìš°í•˜ê³  있지 않습니다.", "account.go_to_profile": "프로필로 ì´ë™", "account.hide_reblogs": "@{name}ì˜ ë¶€ìŠ¤íŠ¸ë¥¼ 숨기기", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} ë‹˜ì´ íŒ”ë¡œìš° ìš”ì²­ì„ ë³´ëƒˆìŠµë‹ˆë‹¤", "account.share": "@{name}ì˜ í”„ë¡œí•„ 공유", "account.show_reblogs": "@{name}ì˜ ë¶€ìŠ¤íŠ¸ 보기", - "account.statuses_counter": "{counter} 게시물", + "account.statuses_counter": "{count, plural, other {{counter} 게시물}}", "account.unblock": "차단 í•´ì œ", "account.unblock_domain": "ë„ë©”ì¸ {domain} 차단 í•´ì œ", "account.unblock_short": "차단 í•´ì œ", diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json index 83fcef26f..5248cdfa5 100644 --- a/app/javascript/mastodon/locales/ku.json +++ b/app/javascript/mastodon/locales/ku.json @@ -32,9 +32,7 @@ "account.follow": "BiÅŸopîne", "account.followers": "Åžopîner", "account.followers.empty": "Kesekî hin ev bikarhêner neÅŸopandiye.", - "account.followers_counter": "{count, plural, one {{counter} Åžopîner} other {{counter} Åžopîner}}", "account.following": "DiÅŸopîne", - "account.following_counter": "{count, plural, one {{counter} DiÅŸopîne} other {{counter} DiÅŸopîne}}", "account.follows.empty": "Ev bikarhêner hin kesekî heya niha neÅŸopandiye.", "account.go_to_profile": "Biçe bo profîlê", "account.hide_reblogs": "Bilindkirinên ji @{name} veşêre", @@ -56,7 +54,6 @@ "account.requested_follow": "{name} dixwaze te biÅŸopîne", "account.share": "Profîla @{name} parve bike", "account.show_reblogs": "Bilindkirinên ji @{name} nîşan bike", - "account.statuses_counter": "{count, plural,one {{counter} Åžandî}other {{counter} Åžandî}}", "account.unblock": "Astengê li ser @{name} rake", "account.unblock_domain": "Astengê li ser navperê {domain} rake", "account.unblock_short": "Astengiyê rake", diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json index 794cbd9ed..1afcf645c 100644 --- a/app/javascript/mastodon/locales/kw.json +++ b/app/javascript/mastodon/locales/kw.json @@ -17,8 +17,6 @@ "account.follow": "Holya", "account.followers": "Holyoryon", "account.followers.empty": "Ny wra nagonan holya'n devnydhyer ma hwath.", - "account.followers_counter": "{count, plural, one {{counter} Holyer} other {{counter} Holyer}}", - "account.following_counter": "{count, plural, one {Ow holya {counter}} other {Ow holya {counter}}}", "account.follows.empty": "Ny wra'n devnydhyer ma holya nagonan hwath.", "account.hide_reblogs": "Kudha kenerthow a @{name}", "account.link_verified_on": "Perghenogeth an kolm ma a veu checkys dhe {date}", @@ -33,7 +31,6 @@ "account.requested": "Ow kortos komendyans. Klyckyewgh dhe hedhi govyn holya", "account.share": "Kevrenna profil @{name}", "account.show_reblogs": "Diskwedhes kenerthow a @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Tout} other {{counter} Tout}}", "account.unblock": "Anlettya @{name}", "account.unblock_domain": "Anlettya gorfarth {domain}", "account.unendorse": "Na wra diskwedhes yn profil", diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json index d867034f0..aa209fcc0 100644 --- a/app/javascript/mastodon/locales/la.json +++ b/app/javascript/mastodon/locales/la.json @@ -14,12 +14,9 @@ "account.edit_profile": "Recolere notionem", "account.featured_tags.last_status_never": "Nulla contributa", "account.featured_tags.title": "Hashtag notÄtÄ« {name}", - "account.followers_counter": "{count, plural, one {{counter} Sectator} other {{counter} Sectatores}}", - "account.following_counter": "{count, plural, one {{counter} Sequens} other {{counter} Sequentes}}", "account.moved_to": "{name} significavit eum suam rationem novam nunc esse:", "account.muted": "Confutatus", "account.requested_follow": "{name} postulavit ut te sequeretur", - "account.statuses_counter": "{count, plural, one {{counter} Nuntius} other {{counter} Nuntii}}", "account.unblock_short": "Solvere impedimentum", "account_note.placeholder": "Click to add a note", "admin.dashboard.retention.average": "Mediocritas", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index bf676a602..292f00818 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -35,9 +35,7 @@ "account.follow_back": "Sige tamyen", "account.followers": "Suivantes", "account.followers.empty": "Por agora dingun no sige a este utilizador.", - "account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}", "account.following": "Sigiendo", - "account.following_counter": "{count, plural, other {Sigiendo a {counter}}}", "account.follows.empty": "Este utilizador ainda no sige a dingun.", "account.go_to_profile": "Va al profil", "account.hide_reblogs": "Eskonde repartajasyones de @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} tiene solisitado segirte", "account.share": "Partaja el profil de @{name}", "account.show_reblogs": "Amostra repartajasyones de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}", "account.unblock": "Dezbloka a @{name}", "account.unblock_domain": "Dezbloka domeno {domain}", "account.unblock_short": "Dezbloka", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index b365d6458..bb69b7339 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -1,18 +1,18 @@ { "about.blocks": "PrižiÅ«rimi serveriai", "about.contact": "Kontaktai:", - "about.disclaimer": "Mastodon – tai nemokama atvirojo kodo programinÄ— įranga ir Mastodon gGmbH prekÄ—s ženklas.", + "about.disclaimer": "„Mastodon“ – tai nemokama atvirojo kodo programinÄ— įranga ir „Mastodon“ gGmbH prekÄ—s ženklas.", "about.domain_blocks.no_reason_available": "Priežastis nepateikta", - "about.domain_blocks.preamble": "Mastodon paprastai leidžia peržiÅ«rÄ—ti turinį ir bendrauti su naudotojais iÅ¡ bet kurio kito fediverse esanÄio serverio. Å ios yra iÅ¡imtys, kurios buvo padarytos Å¡iame konkreÄiame serveryje.", + "about.domain_blocks.preamble": "„Mastodon“ paprastai leidžia peržiÅ«rÄ—ti turinį ir bendrauti su naudotojais iÅ¡ bet kurio kito fediverse esanÄio serverio. Å ios yra iÅ¡imtys, kurios buvo padarytos Å¡iame konkreÄiame serveryje.", "about.domain_blocks.silenced.explanation": "Paprastai nematysi profilių ir turinio iÅ¡ Å¡io serverio, nebent jį aiÅ¡kiai ieÅ¡kosi arba pasirinksi jį sekdamas (-a).", "about.domain_blocks.silenced.title": "Ribota", "about.domain_blocks.suspended.explanation": "Jokie duomenys iÅ¡ Å¡io serverio nebus apdorojami, saugomi ar keiÄiami, todÄ—l bet kokia sÄ…veika ar bendravimas su Å¡io serverio naudotojais bus neįmanomas.", - "about.domain_blocks.suspended.title": "Uždrausta", + "about.domain_blocks.suspended.title": "Pristabdyta", "about.not_available": "Å i informacija nebuvo pateikta Å¡iame serveryje.", - "about.powered_by": "Decentralizuota socialinÄ— medija, kuriÄ… valdo {mastodon}", + "about.powered_by": "Decentralizuota socialinÄ— medija, veikianti pagal „{mastodon}“", "about.rules": "Serverio taisyklÄ—s", "account.account_note_header": "Pastaba", - "account.add_or_remove_from_list": "PridÄ—ti arba iÅ¡trinti iÅ¡ sÄ…rašų", + "account.add_or_remove_from_list": "PridÄ—ti arba paÅ¡alinti iÅ¡ sÄ…rašų", "account.badges.bot": "Automatizuotas", "account.badges.group": "GrupÄ—", "account.block": "Blokuoti @{name}", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 13ceec21c..041072c6a 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -35,9 +35,7 @@ "account.follow_back": "Sekot atpakaļ", "account.followers": "SekotÄji", "account.followers.empty": "Å im lietotÄjam vÄ“l nav sekotÄju.", - "account.followers_counter": "{count, plural, zero {{counter} sekotÄju} one {{counter} sekotÄjs} other {{counter} sekotÄji}}", "account.following": "Seko", - "account.following_counter": "{count, plural, zero{{counter} sekojamo} one {{counter} sekojamais} other {{counter} sekojamie}}", "account.follows.empty": "Å is lietotÄjs pagaidÄm nevienam neseko.", "account.go_to_profile": "Doties uz profilu", "account.hide_reblogs": "PaslÄ“pt @{name} pastiprinÄtos ierakstus", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} nosÅ«tÄ«ja Tev sekoÅ¡anas pieprasÄ«jumu", "account.share": "DalÄ«ties ar @{name} profilu", "account.show_reblogs": "ParÄdÄ«t @{name} pastiprinÄtos ierakstus", - "account.statuses_counter": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}}", "account.unblock": "AtbloÄ·Ä“t @{name}", "account.unblock_domain": "AtbloÄ·Ä“t domÄ“nu {domain}", "account.unblock_short": "AtbloÄ·Ä“t", diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json index d8a470ed4..a09ad98eb 100644 --- a/app/javascript/mastodon/locales/mk.json +++ b/app/javascript/mastodon/locales/mk.json @@ -38,7 +38,6 @@ "account.requested": "Се чека одобрување. Кликни за да одкажиш барање за Ñледење", "account.share": "Сподели @{name} профил", "account.show_reblogs": "Прикажи буÑтови од @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "Одблокирај @{name}", "account.unblock_domain": "Прикажи {domain}", "account.unendorse": "Ðе прикажувај на профил", diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json index 8fb4e818d..d9caccef3 100644 --- a/app/javascript/mastodon/locales/ml.json +++ b/app/javascript/mastodon/locales/ml.json @@ -22,9 +22,7 @@ "account.follow": "പിനàµà´¤àµà´Ÿà´°àµà´•", "account.followers": "പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨à´µàµ¼", "account.followers.empty": "à´ˆ ഉപയോകàµà´¤à´¾à´µà´¿à´¨àµ† ആരàµà´‚ ഇതàµà´µà´°àµ† പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨à´¿à´²àµà´².", - "account.followers_counter": "{count, plural, one {{counter} പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨à´µàµ¼} other {{counter} പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨à´µàµ¼}}", "account.following": "പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨àµ", - "account.following_counter": "{count, plural, one {{counter} പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨àµ} other {{counter} പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨àµ}}", "account.follows.empty": "à´ˆ ഉപയോകàµà´¤à´¾à´µàµ ആരേയàµà´‚ ഇതàµà´µà´°àµ† പിനàµà´¤àµà´Ÿà´°àµà´¨àµà´¨à´¿à´²àµà´².", "account.go_to_profile": "à´ªàµà´°àµŠà´«àµˆà´²à´¿à´²àµ‡à´•àµà´•àµ പോകാം", "account.hide_reblogs": "@{name} ബൂസàµà´±àµà´±àµ ചെയàµà´¤à´µ മറയàµà´•àµà´•", @@ -42,7 +40,6 @@ "account.requested": "à´…à´¨àµà´µà´¾à´¦à´¤àµà´¤à´¿à´¨à´¾à´¯à´¿ കാതàµà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ. പിനàµà´¤àµà´Ÿà´°à´¾à´¨àµà´³àµà´³ അപേകàµà´· റദàµà´¦à´¾à´•àµà´•àµà´µà´¾àµ» ഞെകàµà´•àµà´•", "account.share": "@{name} à´¨àµà´±àµ† à´ªàµà´°àµŠà´«àµˆàµ½ പങàµà´•à´¿à´Ÿàµà´•", "account.show_reblogs": "@{name} ൽ നിനàµà´¨àµà´³àµà´³ ബൂസàµà´±àµà´±àµà´•àµ¾ കാണികàµà´•àµà´•", - "account.statuses_counter": "{count, plural, one {{counter} ടൂടàµà´Ÿàµ} other {{counter} ടൂടàµà´Ÿàµà´•àµ¾}}", "account.unblock": "@{name} തടഞàµà´žà´¤àµ മാറàµà´±àµà´•", "account.unblock_domain": "{domain} à´Žà´¨àµà´¨ മേഖല വെളിപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´•", "account.unblock_short": "അൺബàµà´²àµ‹à´•àµà´•àµ ചെയàµà´¯àµà´•", diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json index c07294d90..2757b96f9 100644 --- a/app/javascript/mastodon/locales/mr.json +++ b/app/javascript/mastodon/locales/mr.json @@ -35,9 +35,7 @@ "account.follow_back": "आपणही अनà¥à¤¸à¤°à¤£ करा", "account.followers": "अनà¥à¤¯à¤¾à¤¯à¥€", "account.followers.empty": "हà¥à¤¯à¤¾ वापरकरà¥à¤¤à¥à¤¯à¤¾à¤šà¤¾ आतापरà¥à¤¯à¤‚त कोणी अनà¥à¤¯à¤¾à¤¯à¥€ नाही.", - "account.followers_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.following": "अनà¥à¤¸à¤°à¤£", - "account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}", "account.follows.empty": "हा वापरकरà¥à¤¤à¤¾ अजूनपरà¥à¤¯à¤‚त कोणाचा अनà¥à¤¯à¤¾à¤¯à¥€ नाही.", "account.go_to_profile": "पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² वर जा", "account.hide_reblogs": "@{name} पासून सरà¥à¤µ बूसà¥à¤Ÿ लपवा", @@ -59,7 +57,6 @@ "account.requested_follow": "{name} ने आपलà¥à¤¯à¤¾à¤²à¤¾ फॉलो करणà¥à¤¯à¤¾à¤šà¥€ रिकà¥à¤µà¥‡à¤¸à¥à¤Ÿ केली आहे", "account.share": "@{name} चे पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² शेअर करा", "account.show_reblogs": "{name}चे सरà¥à¤µ बà¥à¤¸à¥à¤Ÿà¥à¤¸ दाखवा", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "@{name} ला बà¥à¤²à¥‰à¤• करा", "account.unblock_domain": "उघड करा {domain}", "account.unblock_short": "अनबà¥à¤²à¥‰à¤• करा", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 3d7992faf..88c093bde 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -35,9 +35,7 @@ "account.follow_back": "Ikut balik", "account.followers": "Pengikut", "account.followers.empty": "Belum ada yang mengikuti pengguna ini.", - "account.followers_counter": "{count, plural, one {{counter} Pengikut} other {{counter} Pengikut}}", "account.following": "Mengikuti", - "account.following_counter": "{count, plural, one {{counter} Diikuti} other {{counter} Diikuti}}", "account.follows.empty": "Pengguna ini belum mengikuti sesiapa.", "account.go_to_profile": "Pergi ke profil", "account.hide_reblogs": "Sembunyikan galakan daripada @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} has requested to follow you", "account.share": "Kongsi profil @{name}", "account.show_reblogs": "Tunjukkan galakan daripada @{name}", - "account.statuses_counter": "{count, plural, other {{counter} kiriman}}", "account.unblock": "Nyahsekat @{name}", "account.unblock_domain": "Nyahsekat domain {domain}", "account.unblock_short": "Nyahsekat", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index e3287f3f3..46c8d1806 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -34,9 +34,7 @@ "account.follow": "စောင့်ကြည့်", "account.followers": "စောင့်ကြည့်သူများ", "account.followers.empty": "ဤသူကို စောင့်ကြည့်သူ မရှိသေးပါá‹", - "account.followers_counter": "{count, plural, one {စောင့်ကြည့်သူ {counter}} other {စောင့်ကြည့်သူများ {counter}}}", "account.following": "စောင့်ကြည့်နေသည်", - "account.following_counter": "{count, plural, one {စောင့်ကြည့်ထားသူ {counter}} other {စောင့်ကြည့်ထားသူများ {counter}}}", "account.follows.empty": "ဤသူသည် မည်သူ့ကိုမျှ စောင့်ကြည့်á€á€¼á€„်း မရှိသေးပါá‹", "account.go_to_profile": "ပရိုဖိုင်းသို့ သွားရန်", "account.hide_reblogs": "@{name} á မျှá€á€±á€™á€¾á€¯á€€á€­á€¯ á€á€¾á€€á€ºá€‘ားရန်", @@ -61,7 +59,6 @@ "account.requested_follow": "{name} က သင့်ကို စောင့်ကြည့်ရန် á€á€±á€¬á€„်းဆိုထားသည်", "account.share": "{name}áပရိုဖိုင်ကိုမျှá€á€±á€•á€«", "account.show_reblogs": "@{name} မှ မျှá€á€±á€™á€¾á€¯á€™á€»á€¬á€¸á€€á€­á€¯ ပြပါ\n", - "account.statuses_counter": "{count, plural, one {{counter} ပိုစ့်များ} other {{counter} ပိုစ့်များ}}", "account.unblock": "{name} ကို ဘလော့ဖြုá€á€ºá€™á€Šá€º", "account.unblock_domain": " {domain} ဒိုမိန်းကိုပြန်ဖွင့်မည်", "account.unblock_short": "ဘလော့ဖြုá€á€ºá€›á€”်", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json index 500261a34..ca23a1f78 100644 --- a/app/javascript/mastodon/locales/ne.json +++ b/app/javascript/mastodon/locales/ne.json @@ -39,7 +39,6 @@ "account.requested_follow": "{name} ले तपाईंलाई फलो गरà¥à¤¨ अनà¥à¤°à¥‹à¤§ गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ छ", "account.share": "@{name} को पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² सेयर गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥", "account.show_reblogs": "@{name} को बूसà¥à¤Ÿà¤¹à¤°à¥‚ देखाउनà¥à¤¹à¥‹à¤¸à¥", - "account.statuses_counter": "{count, plural, one {{counter} पोसà¥à¤Ÿ} other {{counter} पोसà¥à¤Ÿà¤¹à¤°à¥‚}}", "account.unblock": "@{name} लाई अनबà¥à¤²à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥", "account.unblock_domain": "{domain} डोमेनलाई अनबà¥à¤²à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥", "account.unblock_short": "अनबà¥à¤²à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 93b44f29a..0fb0edf0a 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -35,9 +35,9 @@ "account.follow_back": "Fylg tilbake", "account.followers": "Fylgjarar", "account.followers.empty": "Ingen fylgjer denne brukaren enno.", - "account.followers_counter": "{count, plural, one {{counter} fylgjar} other {{counter} fylgjarar}}", + "account.followers_counter": "{count, plural, one {{counter} følgjar} other {{counter} følgjarar}}", "account.following": "Fylgjer", - "account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}", + "account.following_counter": "{count, plural, one {{counter} følgjer} other {{counter} følgjer}}", "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.", "account.go_to_profile": "GÃ¥ til profil", "account.hide_reblogs": "Gøym framhevingar frÃ¥ @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} har bedt om Ã¥ fÃ¥ fylgja deg", "account.share": "Del @{name} sin profil", "account.show_reblogs": "Vis framhevingar frÃ¥ @{name}", - "account.statuses_counter": "{count, plural, one {{counter} tut} other {{counter} tut}}", + "account.statuses_counter": "{count, plural, one {{counter} innlegg} other {{counter} innlegg}}", "account.unblock": "Stopp blokkering av @{name}", "account.unblock_domain": "Stopp blokkering av domenet {domain}", "account.unblock_short": "Stopp blokkering", @@ -696,8 +696,11 @@ "server_banner.about_active_users": "Personar som har brukt denne tenaren dei siste 30 dagane (MÃ¥nadlege Aktive Brukarar)", "server_banner.active_users": "aktive brukarar", "server_banner.administered_by": "Administrert av:", + "server_banner.is_one_of_many": "{domain} er ein av dei mange uavhengige Mastodon-serverane du kan bruke til Ã¥ delta i Fødiverset.", "server_banner.server_stats": "Tenarstatistikk:", "sign_in_banner.create_account": "Opprett konto", + "sign_in_banner.follow_anyone": "Følg kven som helst pÃ¥ tvers av Fødiverset og sjÃ¥ alt i kronologisk rekkjefølgje. Ingen algoritmar, reklamar eller clickbait i sikte.", + "sign_in_banner.mastodon_is": "Mastodon er den beste mÃ¥ten Ã¥ følgje med pÃ¥ det som skjer.", "sign_in_banner.sign_in": "Logg inn", "sign_in_banner.sso_redirect": "Logg inn eller registrer deg", "status.admin_account": "Opne moderasjonsgrensesnitt for @{name}", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 213ba8af1..2bda37340 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -35,9 +35,7 @@ "account.follow_back": "Følg tilbake", "account.followers": "Følgere", "account.followers.empty": "Ingen følger denne brukeren ennÃ¥.", - "account.followers_counter": "{count, plural, one {{counter} følger} other {{counter} følgere}}", "account.following": "Følger", - "account.following_counter": "{count, plural, one {{counter} som følges} other {{counter} som følges}}", "account.follows.empty": "Denne brukeren følger ikke noen enda.", "account.go_to_profile": "GÃ¥ til profil", "account.hide_reblogs": "Skjul fremhevinger fra @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} har bedt om Ã¥ fÃ¥ følge deg", "account.share": "Del @{name} sin profil", "account.show_reblogs": "Vis fremhevinger fra @{name}", - "account.statuses_counter": "{count, plural, one {{counter} innlegg} other {{counter} innlegg}}", "account.unblock": "Opphev blokkering av @{name}", "account.unblock_domain": "Opphev blokkering av {domain}", "account.unblock_short": "Opphev blokkering", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index d8e114158..d977eed4a 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -32,9 +32,7 @@ "account.follow_back": "Sègre en retorn", "account.followers": "Seguidors", "account.followers.empty": "Degun sèc pas aqueste utilizaire pel moment.", - "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidors}}", "account.following": "Abonat", - "account.following_counter": "{count, plural, one {{counter} Abonaments} other {{counter} Abonaments}}", "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.", "account.go_to_profile": "Anar al perfil", "account.hide_reblogs": "Rescondre los partatges de @{name}", @@ -60,7 +58,6 @@ "account.requested_follow": "{name} a demandat a vos sègre", "account.share": "Partejar lo perfil a @{name}", "account.show_reblogs": "Mostrar los partatges de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Tut} other {{counter} Tuts}}", "account.unblock": "Desblocar @{name}", "account.unblock_domain": "Desblocar {domain}", "account.unblock_short": "Desblocat", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 46924d737..3828ff887 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -25,9 +25,7 @@ "account.follow_back": "ਵਾਪਸ ਫਾਲ਼ੋ ਕਰੋ", "account.followers": "ਫ਼ਾਲੋਅਰ", "account.followers.empty": "ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਹਾਲੇ ਕੋਈ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।", - "account.followers_counter": "{count, plural, one {{counter} ਫ਼ਾਲੋਅਰ} other {{counter} ਫ਼ਾਲੋਅਰ}}", "account.following": "ਫ਼ਾਲੋ ਕੀਤਾ", - "account.following_counter": "{count, plural, one {{counter} ਨੂੰ ਫ਼ਾਲੋ} other {{counter} ਨੂੰ ਫ਼ਾਲੋ}}", "account.follows.empty": "ਇਹ ਵਰਤੋਂਕਾਰ ਹਾਲੇ ਕਿਸੇ ਨੂੰ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।", "account.go_to_profile": "ਪਰੋਫਾਇਲ ਉੱਤੇ ਜਾਓ", "account.media": "ਮੀਡੀਆ", @@ -41,7 +39,6 @@ "account.requested": "ਮਨਜ਼ੂਰੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ। ਫ਼ਾਲੋ ਬੇਨਤੀਆਂ ਨੂੰ ਰੱਦ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ", "account.requested_follow": "{name} ਨੇ ਤà©à¨¹à¨¾à¨¨à©‚à©° ਫ਼ਾਲੋ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ", "account.share": "{name} ਦਾ ਪਰੋਫ਼ਾਇਲ ਸਾਂà¨à¨¾ ਕਰੋ", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "@{name} ਤੋਂ ਪਾਬੰਦੀ ਹਟਾਓ", "account.unblock_domain": "{domain} ਡੋਮੇਨ ਤੋਂ ਪਾਬੰਦੀ ਹਟਾਓ", "account.unblock_short": "ਪਾਬੰਦੀ ਹਟਾਓ", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index afe549054..34d0ba36e 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -35,9 +35,7 @@ "account.follow_back": "Seguir de volta", "account.followers": "Seguidores", "account.followers.empty": "Nada aqui.", - "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", "account.following": "Seguindo", - "account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}", "account.follows.empty": "Nada aqui.", "account.go_to_profile": "Ir ao perfil", "account.hide_reblogs": "Ocultar boosts de @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} quer te seguir", "account.share": "Compartilhar perfil de @{name}", "account.show_reblogs": "Mostrar boosts de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear domínio {domain}", "account.unblock_short": "Desbloquear", @@ -415,6 +412,7 @@ "limited_account_hint.title": "Este perfil foi ocultado pelos moderadores do {domain}.", "link_preview.author": "Por {name}", "link_preview.more_from_author": "Mais de {name}", + "link_preview.shares": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "lists.account.add": "Adicionar à lista", "lists.account.remove": "Remover da lista", "lists.delete": "Excluir lista", @@ -695,8 +693,11 @@ "server_banner.about_active_users": "Pessoas usando este servidor durante os últimos 30 dias (Usuários ativos mensalmente)", "server_banner.active_users": "usuários ativos", "server_banner.administered_by": "Administrado por:", + "server_banner.is_one_of_many": "{domain} é um dos muitos servidores Mastodon independentes que você pode usar para participar do fediverso.", "server_banner.server_stats": "Estatísticas do servidor:", "sign_in_banner.create_account": "Criar conta", + "sign_in_banner.follow_anyone": "Siga alguém pelo fediverso e veja tudo em ordem cronológica. Sem algoritmos, anúncios ou clickbait à vista.", + "sign_in_banner.mastodon_is": "O Mastodon é a melhor maneira de acompanhar o que está acontecendo.", "sign_in_banner.sign_in": "Entrar", "sign_in_banner.sso_redirect": "Entrar ou Registrar-se", "status.admin_account": "Abrir interface de moderação para @{name}", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 9446d5ee2..6a6feca30 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -37,7 +37,7 @@ "account.followers.empty": "Ainda ninguém segue este utilizador.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", "account.following": "A seguir", - "account.following_counter": "{count, plural, other {A seguir {counter}}}", + "account.following_counter": "{count, plural, one {A seguir {counter}} other {A seguir {counter}}}", "account.follows.empty": "Este utilizador ainda não segue ninguém.", "account.go_to_profile": "Ir para o perfil", "account.hide_reblogs": "Esconder partilhas de @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} pediu para segui-lo", "account.share": "Partilhar o perfil @{name}", "account.show_reblogs": "Mostrar partilhas de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", + "account.statuses_counter": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear o domínio {domain}", "account.unblock_short": "Desbloquear", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index 3a2fab905..35abf1b02 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -35,9 +35,7 @@ "account.follow_back": "UrmăreÅŸte înapoi", "account.followers": "Urmăritori", "account.followers.empty": "Acest utilizator nu are încă urmăritori.", - "account.followers_counter": "{count, plural, one {Un abonat} few {{counter} abonaÈ›i} other {{counter} de abonaÈ›i}}", "account.following": "UrmăriÈ›i", - "account.following_counter": "{count, plural, one {Un abonament} few {{counter} abonamente} other {{counter} de abonamente}}", "account.follows.empty": "Momentan acest utilizator nu are niciun abonament.", "account.go_to_profile": "Mergi la profil", "account.hide_reblogs": "Ascunde distribuirile de la @{name}", @@ -62,7 +60,6 @@ "account.requested_follow": "{name} A cerut să vă urmărească", "account.share": "Distribuie profilul lui @{name}", "account.show_reblogs": "AfiÈ™ează distribuirile de la @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "Deblochează pe @{name}", "account.unblock_domain": "Deblochează domeniul {domain}", "account.unblock_short": "Deblochează", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 40ca84814..97a1f0b09 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -35,9 +35,7 @@ "account.follow_back": "ПодпиÑатьÑÑ Ð² ответ", "account.followers": "ПодпиÑчики", "account.followers.empty": "Ðа Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¿Ð¾ÐºÐ° никто не подпиÑан.", - "account.followers_counter": "{count, plural, one {{counter} подпиÑчик} many {{counter} подпиÑчиков} other {{counter} подпиÑчика}}", "account.following": "ПодпиÑки", - "account.following_counter": "{count, plural, one {{counter} подпиÑка} many {{counter} подпиÑок} other {{counter} подпиÑки}}", "account.follows.empty": "Этот пользователь пока ни на кого не подпиÑалÑÑ.", "account.go_to_profile": "Перейти к профилю", "account.hide_reblogs": "Скрыть Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ‚ @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} отправил(а) вам Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку", "account.share": "ПоделитьÑÑ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»ÐµÐ¼ @{name}", "account.show_reblogs": "Показывать Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ‚ @{name}", - "account.statuses_counter": "{count, plural, one {{counter} поÑÑ‚} many {{counter} поÑтов} other {{counter} поÑта}}", "account.unblock": "Разблокировать @{name}", "account.unblock_domain": "Разблокировать {domain}", "account.unblock_short": "Разблокировать", diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json index 58654deb0..c3880a6b0 100644 --- a/app/javascript/mastodon/locales/sa.json +++ b/app/javascript/mastodon/locales/sa.json @@ -32,9 +32,7 @@ "account.follow": "अनà¥à¤¸à¥à¤°à¤¿à¤¯à¤¤à¤¾à¤®à¥", "account.followers": "अनà¥à¤¸à¤°à¥à¤¤à¤¾à¤°à¤ƒ", "account.followers.empty": "नाऽनà¥à¤¸à¤°à¥à¤¤à¤¾à¤°à¥‹ वरà¥à¤¤à¤¨à¥à¤¤à¥‡", - "account.followers_counter": "{count, plural, one {{counter} अनà¥à¤¸à¤°à¥à¤¤à¤¾} two {{counter} अनà¥à¤¸à¤°à¥à¤¤à¤¾à¤°à¥Œ} other {{counter} अनà¥à¤¸à¤°à¥à¤¤à¤¾à¤°à¤ƒ}}", "account.following": "अनà¥à¤¸à¤°à¤¤à¤¿", - "account.following_counter": "{count, plural, one {{counter} अनà¥à¤¸à¥ƒà¤¤à¤ƒ} two {{counter} अनà¥à¤¸à¥ƒà¤¤à¥Œ} other {{counter} अनà¥à¤¸à¥ƒà¤¤à¤¾à¤ƒ}}", "account.follows.empty": "न कोऽपà¥à¤¯à¤¨à¥à¤¸à¥ƒà¤¤à¥‹ वरà¥à¤¤à¤¤à¥‡", "account.go_to_profile": "पà¥à¤°à¥‹à¤«à¤¾à¤¯à¤¿à¤²à¤‚ गचà¥à¤›", "account.hide_reblogs": "@{name} मितà¥à¤°à¤¸à¥à¤¯ पà¥à¤°à¤•à¤¾à¤¶à¤¨à¤¾à¤¨à¤¿ छिदà¥à¤¯à¤¨à¥à¤¤à¤¾à¤®à¥", @@ -56,7 +54,6 @@ "account.requested_follow": "{name} तà¥à¤µà¤¾à¤®à¤¨à¥à¤¸à¤°à¥à¤¤à¥à¤®à¤¯à¤¾à¤šà¥€à¤¤à¥", "account.share": "@{name} मितà¥à¤°à¤¸à¥à¤¯ विवरणं विभाजà¥à¤¯à¤¤à¤¾à¤®à¥", "account.show_reblogs": "@{name} मितà¥à¤°à¤¸à¥à¤¯ पà¥à¤°à¤•à¤¾à¤¶à¤¨à¤¾à¤¨à¤¿ दृशà¥à¤¯à¤¨à¥à¤¤à¤¾à¤®à¥", - "account.statuses_counter": "{count, plural, one {{counter} पतà¥à¤°à¤®à¥} two{{counter} पतà¥à¤°à¥‡} other {{counter} पतà¥à¤°à¤¾à¤£à¤¿}}", "account.unblock": "निषेधता नशà¥à¤¯à¤¤à¤¾à¤®à¥ @{name}", "account.unblock_domain": "पà¥à¤°à¤¦à¥‡à¤¶à¤¨à¤¿à¤·à¥‡à¤§à¤¤à¤¾ नशà¥à¤¯à¤¤à¤¾à¤®à¥ {domain}", "account.unblock_short": "अनवरà¥à¤¨à¥à¤§à¤¿", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index a0b5b3271..895557373 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -26,9 +26,7 @@ "account.follow": "Sighi", "account.followers": "Sighiduras", "account.followers.empty": "Nemos sighit ancora custa persone.", - "account.followers_counter": "{count, plural, one {{counter} sighidura} other {{counter} sighiduras}}", "account.following": "Sighende", - "account.following_counter": "{count, plural, one {Sighende a {counter}} other {Sighende a {counter}}}", "account.follows.empty": "Custa persone non sighit ancora a nemos.", "account.hide_reblogs": "Cua is cumpartziduras de @{name}", "account.in_memoriam": "In memoriam.", @@ -47,7 +45,6 @@ "account.requested_follow": "{name} at dimandadu de ti sighire", "account.share": "Cumpartzi su profilu de @{name}", "account.show_reblogs": "Ammustra is cumpartziduras de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} publicatzione} other {{counter} publicatziones}}", "account.unblock": "Isbloca a @{name}", "account.unblock_domain": "Isbloca su domìniu {domain}", "account.unendorse": "Non cussiges in su profilu", diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json index 53501a593..397f63fed 100644 --- a/app/javascript/mastodon/locales/sco.json +++ b/app/javascript/mastodon/locales/sco.json @@ -31,9 +31,7 @@ "account.follow": "Follae", "account.followers": "Follaers", "account.followers.empty": "Naebody follaes this uiser yit.", - "account.followers_counter": "{count, plural, one {{counter} Follaer} other {{counter} Follaers}}", "account.following": "Follaein", - "account.following_counter": "{count, plural, one {{counter} Follaein} other {{counter} Follaein}}", "account.follows.empty": "This uiser disnae follae oniebody yit.", "account.go_to_profile": "Gang tae profile", "account.hide_reblogs": "Dinnae shaw heezes fae @{name}", @@ -53,7 +51,6 @@ "account.requested": "Haudin fir approval. Chap tae cancel follae request", "account.share": "Share @{name}'s profile", "account.show_reblogs": "Shaw heezes frae @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}", "account.unblock": "Undingie @{name}", "account.unblock_domain": "Undingie domain {domain}", "account.unblock_short": "Undingie", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index 22320daef..fbfdfaa65 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -23,9 +23,7 @@ "account.follow": "අනුගමනය", "account.followers": "අනුගà·à¶¸à·’කයින්", "account.followers.empty": "කිසිවෙක් අනුගමනය කර නà·à¶­.", - "account.followers_counter": "{count, plural, one {අනුගà·à¶¸à·’කයින් {counter}} other {අනුගà·à¶¸à·’කයින් {counter}}}", "account.following": "අනුගමන", - "account.following_counter": "{count, plural, one {අනුගමන {counter}} other {අනුගමන {counter}}}", "account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.", "account.go_to_profile": "පà·à¶­à·’කඩට යන්න", "account.joined_short": "එක් වූ දිනය", @@ -39,7 +37,6 @@ "account.posts_with_replies": "ලිපි සහ පිළිතුරු", "account.report": "@{name} à·€à·à¶»à·Šà¶­à· කරන්න", "account.share": "@{name} ගේ පà·à¶­à·’කඩ බෙදà·à¶œà¶±à·Šà¶±", - "account.statuses_counter": "{count, plural, one {ලිපි {counter}} other {ලිපි {counter}}}", "account.unblock": "@{name} අනවහිර කරන්න", "account.unblock_domain": "{domain} වසම අනවහිර කරන්න", "account.unblock_short": "අනවහිර", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 4c152a214..ed877f766 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -35,9 +35,7 @@ "account.follow_back": "SledovaÅ¥ späť", "account.followers": "Sledovatelia", "account.followers.empty": "Tento úÄet eÅ¡te nikto nesleduje.", - "account.followers_counter": "{count, plural, one {{counter} sledujúci úÄet} few {{counter} sledujúce úÄty} many {{counter} sledujúcich úÄtov} other {{counter} sledujúcich úÄtov}}", "account.following": "Sledovaný úÄet", - "account.following_counter": "{count, plural, one {{counter} sledovaný úÄet} few {{counter} sledované úÄty} many {{counter} sledovaných úÄtov} other {{counter} sledovaných úÄtov}}", "account.follows.empty": "Tento úÄet eÅ¡te nikoho nesleduje.", "account.go_to_profile": "PrejsÅ¥ na profil", "account.hide_reblogs": "SkryÅ¥ zdieľania od @{name}", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} vás chce sledovaÅ¥", "account.share": "Zdieľaj profil @{name}", "account.show_reblogs": "ZobrazovaÅ¥ zdieľania od @{name}", - "account.statuses_counter": "{count, plural, one {{counter} príspevok} few {{counter} príspevky} many {{counter} príspevkov} other {{counter} príspevkov}}", "account.unblock": "OdblokovaÅ¥ @{name}", "account.unblock_domain": "OdblokovaÅ¥ doménu {domain}", "account.unblock_short": "OdblokovaÅ¥", @@ -91,7 +88,10 @@ "audio.hide": "SkryÅ¥ zvuk", "block_modal.show_less": "ZobraziÅ¥ menej", "block_modal.show_more": "ZobraziÅ¥ viac", + "block_modal.they_cant_mention": "Nemôžu Å¥a spomenúť, alebo nasledovaÅ¥.", + "block_modal.they_will_know": "Môžu vidieÅ¥, že sú zablokovaní/ý.", "block_modal.title": "BlokovaÅ¥ užívateľa?", + "block_modal.you_wont_see_mentions": "Neuvidíš príspevky, ktoré ich spomínajú.", "boost_modal.combo": "Nabudúce môžete preskoÄiÅ¥ stlaÄením {combo}", "bundle_column_error.copy_stacktrace": "KopírovaÅ¥ chybovú hlášku", "bundle_column_error.error.body": "Požadovanú stránku nebolo možné vykresliÅ¥. Môže to byÅ¥ spôsobené chybou v naÅ¡om kóde alebo problémom s kompatibilitou prehliadaÄa.", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 195797143..2a3d74a80 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -35,9 +35,9 @@ "account.follow_back": "Sledi nazaj", "account.followers": "Sledilci", "account.followers.empty": "NihÄe ne sledi temu uporabniku.", - "account.followers_counter": "{count, plural, one {ima {counter} sledilca} two {ima {counter} sledilca} few {ima {counter} sledilce} other {ima {counter} sledilcev}}", + "account.followers_counter": "{count, plural, one {{counter} sledilec} two {{counter} sledilca} few {{counter} sledilci} other {{counter} sledilcev}}", "account.following": "Sledim", - "account.following_counter": "{count, plural, one {sledi {count} osebi} two {sledi {count} osebama} few {sledi {count} osebam} other {sledi {count} osebam}}", + "account.following_counter": "{count, plural, one {{counter} sleden} two {{counter} sledena} few {{counter} sledeni} other {{counter} sledenih}}", "account.follows.empty": "Ta uporabnik Å¡e ne sledi nikomur.", "account.go_to_profile": "Pojdi na profil", "account.hide_reblogs": "Skrij izpostavitve od @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} vam želi slediti", "account.share": "Deli profil osebe @{name}", "account.show_reblogs": "Pokaži izpostavitve osebe @{name}", - "account.statuses_counter": "{count, plural, one {{count} objava} two {{count} objavi} few {{count} objave} other {{count} objav}}", + "account.statuses_counter": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objave} other {{counter} objav}}", "account.unblock": "Odblokiraj @{name}", "account.unblock_domain": "Odblokiraj domeno {domain}", "account.unblock_short": "Odblokiraj", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 6903bceff..96b7b3fef 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -35,9 +35,9 @@ "account.follow_back": "Ndiqe gjithashtu", "account.followers": "Ndjekës", "account.followers.empty": "Këtë përdorues ende s’e ndjek kush.", - "account.followers_counter": "{count, plural, one {{counter} Ndjekës} other {{counter} Ndjekës}}", + "account.followers_counter": "{count, plural, one {{counter} ndjekës} other {{counter} ndjekës}}", "account.following": "Ndjekje", - "account.following_counter": "{count, plural, one {{counter} i Ndjekur} other {{counter} të Ndjekur}}", + "account.following_counter": "{count, plural, one {{counter} i ndjekur} other {{counter} të ndjekur}}", "account.follows.empty": "Ky përdorues ende s’ndjek kënd.", "account.go_to_profile": "Kalo te profili", "account.hide_reblogs": "Fshih përforcime nga @{name}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} ka kërkuar t’ju ndjekë", "account.share": "Ndajeni profilin e @{name} me të tjerët", "account.show_reblogs": "Shfaq përforcime nga @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Mesazh} other {{counter} Mesazhe}}", + "account.statuses_counter": "{count, plural, one {{counter} postim} other {{counter} postime}}", "account.unblock": "Zhbllokoje @{name}", "account.unblock_domain": "Zhblloko përkatësinë {domain}", "account.unblock_short": "Zhbllokoje", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index 63b2e03c9..71b69d428 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -63,7 +63,7 @@ "account.requested_follow": "{name} je zatražio da vas prati", "account.share": "Podeli profil korisnika @{name}", "account.show_reblogs": "Prikaži podržavanja od korisnika @{name}", - "account.statuses_counter": "{count, plural, one {{counter} objavio} few {{counter} objavio} other {{counter} objavio}}", + "account.statuses_counter": "{count, plural, one {{counter} objava} few {{counter} objave} other {{counter} objava}}", "account.unblock": "Odblokiraj korisnika @{name}", "account.unblock_domain": "Odblokiraj domen {domain}", "account.unblock_short": "Odblokiraj", @@ -696,8 +696,11 @@ "server_banner.about_active_users": "Ljudi koji su koristili ovaj server u prethodnih 30 dana (meseÄno aktivnih korisnika)", "server_banner.active_users": "aktivnih korisnika", "server_banner.administered_by": "Administrira:", + "server_banner.is_one_of_many": "{domain} je jedan od mnogih nezavisnih Mastodon servera koje možete koristiti za uÄeÅ¡cÌe u fediverzumu.", "server_banner.server_stats": "Statistike servera:", "sign_in_banner.create_account": "Napravite nalog", + "sign_in_banner.follow_anyone": "Pratite bilo koga Å¡irom fediverzuma i pogledajte sve hronoloÅ¡kim redom. Nema algoritama, reklama ili mamaca za klikove na vidiku.", + "sign_in_banner.mastodon_is": "Mastodon je najbolji naÄin da budete u toku sa onim Å¡to se deÅ¡ava.", "sign_in_banner.sign_in": "Prijavite se", "sign_in_banner.sso_redirect": "Prijavite se ili se registrujte", "status.admin_account": "Otvori moderatorsko okruženje za @{name}", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index c6b969e98..2c4649f9d 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -63,7 +63,7 @@ "account.requested_follow": "{name} је затражио да Ð²Ð°Ñ Ð¿Ñ€Ð°Ñ‚Ð¸", "account.share": "Подели профил кориÑника @{name}", "account.show_reblogs": "Прикажи подржавања од кориÑника @{name}", - "account.statuses_counter": "{count, plural, one {{counter} објавио} few {{counter} објавио} other {{counter} објавио}}", + "account.statuses_counter": "{count, plural, one {{counter} објава} few {{counter} објаве} other {{counter} објава}}", "account.unblock": "Одблокирај кориÑника @{name}", "account.unblock_domain": "Одблокирај домен {domain}", "account.unblock_short": "Одблокирај", @@ -696,8 +696,11 @@ "server_banner.about_active_users": "Људи који Ñу кориÑтили овај Ñервер у претходних 30 дана (меÑечно активних кориÑника)", "server_banner.active_users": "активних кориÑника", "server_banner.administered_by": "ÐдминиÑтрира:", + "server_banner.is_one_of_many": "{domain} је један од многих незавиÑних Mastodon Ñервера које можете кориÑтити за учешће у федиверзуму.", "server_banner.server_stats": "СтатиÑтике Ñервера:", "sign_in_banner.create_account": "Ðаправите налог", + "sign_in_banner.follow_anyone": "Пратите било кога широм федиверзума и погледајте Ñве хронолошким редом. Ðема алгоритама, реклама или мамаца за кликове на видику.", + "sign_in_banner.mastodon_is": "Mastodon је најбољи начин да будете у току Ñа оним што Ñе дешава.", "sign_in_banner.sign_in": "Пријавите Ñе", "sign_in_banner.sso_redirect": "Пријавите Ñе или Ñе региÑтрујте", "status.admin_account": "Отвори модераторÑко окружење за @{name}", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index ced6c3605..1833a2cfd 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -37,7 +37,6 @@ "account.followers.empty": "Ingen följer denna användare än.", "account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}", "account.following": "Följer", - "account.following_counter": "{count, plural, one {{counter} följd} other {{counter} följda}}", "account.follows.empty": "Denna användare följer inte nÃ¥gon än.", "account.go_to_profile": "GÃ¥ till profilen", "account.hide_reblogs": "Dölj boostar frÃ¥n @{name}", diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json index 43cfc78d5..34d086eb4 100644 --- a/app/javascript/mastodon/locales/szl.json +++ b/app/javascript/mastodon/locales/szl.json @@ -23,7 +23,6 @@ "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.requested": "Awaiting approval", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account_note.placeholder": "Click to add a note", "column.pins": "Pinned toot", "community.column_settings.media_only": "Media only", diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json index ac0984293..d44ac424f 100644 --- a/app/javascript/mastodon/locales/ta.json +++ b/app/javascript/mastodon/locales/ta.json @@ -24,9 +24,7 @@ "account.follow_back": "பினà¯à®¤à¯Šà®Ÿà®°à¯", "account.followers": "பினà¯à®¤à¯Šà®Ÿà®°à¯à®ªà®µà®°à¯à®•à®³à¯", "account.followers.empty": "இதà¯à®µà®°à¯ˆ யாரà¯à®®à¯ இநà¯à®¤ பயனரைப௠பினà¯à®¤à¯Šà®Ÿà®°à®µà®¿à®²à¯à®²à¯ˆ.", - "account.followers_counter": "{count, plural, one {{counter} வாசகரà¯} other {{counter} வாசகரà¯à®•à®³à¯}}", "account.following": "பினà¯à®¤à¯Šà®Ÿà®°à¯à®®à¯", - "account.following_counter": "{count, plural,one {{counter} சநà¯à®¤à®¾} other {{counter} சநà¯à®¤à®¾à®•à¯à®•à®³à¯}}", "account.follows.empty": "இநà¯à®¤ பயனர௠இதà¯à®µà®°à¯ˆ யாரையà¯à®®à¯ பினà¯à®¤à¯Šà®Ÿà®°à®µà®¿à®²à¯à®²à¯ˆ.", "account.go_to_profile": "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à®¿à®±à¯à®•à¯à®šà¯ செலà¯à®²à®µà¯à®®à¯", "account.hide_reblogs": "இரà¯à®¨à¯à®¤à¯ ஊகà¯à®•à®¿à®¯à®¾à®• மறை @{name}", @@ -45,7 +43,6 @@ "account.requested": "ஒபà¯à®ªà¯à®¤à®²à¯à®•à¯à®•à®¾à®•à®•à¯ காதà¯à®¤à®¿à®°à¯à®•à¯à®•à®¿à®±à®¤à¯. பினà¯à®¤à¯Šà®Ÿà®°à¯à®®à¯ கோரிகà¯à®•à¯ˆà®¯à¯ˆ நீகà¯à®• à®…à®´à¯à®¤à¯à®¤à®µà¯à®®à¯", "account.share": "@{name} உடைய விவரதà¯à®¤à¯ˆ பகிரà¯", "account.show_reblogs": "காடà¯à®Ÿà¯ boosts இரà¯à®¨à¯à®¤à¯ @{name}", - "account.statuses_counter": "{count, plural, one {{counter} டூடà¯} other {{counter} டூடà¯à®Ÿà¯à®•à®³à¯}}", "account.unblock": "@{name} மீத௠தடை நீகà¯à®•à¯à®•", "account.unblock_domain": "{domain} ஠காணà¯à®ªà®¿", "account.unblock_short": "தடையை நீகà¯à®•à¯", diff --git a/app/javascript/mastodon/locales/tai.json b/app/javascript/mastodon/locales/tai.json index 825cfb93b..cad6e8eaa 100644 --- a/app/javascript/mastodon/locales/tai.json +++ b/app/javascript/mastodon/locales/tai.json @@ -9,7 +9,6 @@ "account.posts": "Huah-siann", "account.posts_with_replies": "Huah-siann kah huê-ìng", "account.requested": "Tán-thÄi phue-tsún", - "account.statuses_counter": "{count, plural, one {{counter} Huah-siann} other {{counter} Huah-siann}}", "account_note.placeholder": "Tiám tsiÌt-Ä“ ka-thiam pÄ«-tsù", "column.pins": "Tah thâu-tsîng ê huah-siann", "community.column_settings.media_only": "Kan-na muî-thé", diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json index 284102c38..c06472561 100644 --- a/app/javascript/mastodon/locales/te.json +++ b/app/javascript/mastodon/locales/te.json @@ -25,7 +25,6 @@ "account.requested": "ఆమోదం కోసం వేచి ఉంది. à°…à°­à±à°¯à°°à±à°¥à°¨à°¨à± à°°à°¦à±à°¦à± చేయడానికి à°•à±à°²à°¿à°•à± చేయండి", "account.share": "@{name} యొకà±à°• à°ªà±à°°à±Šà°«à±ˆà°²à±à°¨à± పంచà±à°•à±‹à°‚à°¡à°¿", "account.show_reblogs": "@{name}à°¨à±à°‚à°šà°¿ బూసà±à°Ÿà± లనౠచూపించà±", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "@{name}పై à°¬à±à°²à°¾à°•à± నౠతొలగించà±", "account.unblock_domain": "{domain}నౠదాచవదà±à°¦à±", "account.unendorse": "à°ªà±à°°à±Šà°«à±ˆà°²à±à°²à±‹ చూపించవదà±à°¦à±", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 0bb2a0e4a..ac39a3fd7 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -35,9 +35,9 @@ "account.follow_back": "Geri takip et", "account.followers": "Takipçi", "account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.", - "account.followers_counter": "{count, plural, one {{counter} Takipçi} other {{counter} Takipçi}}", + "account.followers_counter": "{count, plural, one {{counter} takipçi} other {{counter} takipçi}}", "account.following": "Takip Ediliyor", - "account.following_counter": "{count, plural, one {{counter} Takip Edilen} other {{counter} Takip Edilen}}", + "account.following_counter": "{count, plural, one {{counter} takip edilen} other {{counter} takip edilen}}", "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.", "account.go_to_profile": "Profile git", "account.hide_reblogs": "@{name} kiÅŸisinin boostlarını gizle", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} size takip isteÄŸi gönderdi", "account.share": "@{name} adlı kiÅŸinin profilini paylaÅŸ", "account.show_reblogs": "@{name} kiÅŸisinin yeniden paylaşımlarını göster", - "account.statuses_counter": "{count, plural, one {{counter} Gönderi} other {{counter} Gönderi}}", + "account.statuses_counter": "{count, plural, one {{counter} gönderi} other {{counter} gönderi}}", "account.unblock": "@{name} adlı kiÅŸinin engelini kaldır", "account.unblock_domain": "{domain} alan adının engelini kaldır", "account.unblock_short": "Engeli kaldır", diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json index 273c1a6de..baba3190d 100644 --- a/app/javascript/mastodon/locales/tt.json +++ b/app/javascript/mastodon/locales/tt.json @@ -31,9 +31,7 @@ "account.follow": "Язылу", "account.followers": "Язылучы", "account.followers.empty": "Әле беркем дә Ñзылмаган.", - "account.followers_counter": "{count, plural,one {{counter} Ñзылучы} other {{counter} Ñзылучы}}", "account.following": "Язылулар", - "account.following_counter": "{count, plural, one {{counter} Ñзылу} other {{counter} Ñзылу}}", "account.follows.empty": "Беркемгә дә Ñзылмаган әле.", "account.go_to_profile": "Профильгә күчү", "account.hide_reblogs": "Скрывать көчен нче @{name}", @@ -55,7 +53,6 @@ "account.requested_follow": "{name} Сезгә Ñзылу Ñоравын җиберде", "account.share": "@{name} профиле белән уртаклашу", "account.show_reblogs": "КүрÑәтергә көчәйтү нче @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Ñзма} other {{counter} Ñзма}}", "account.unblock": "@{name} бикләвен чыгу", "account.unblock_domain": "{domain} бикләвен чыгу", "account.unblock_short": "Бикләүне чыгу", diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json index 4120d4483..e3dd0e6b1 100644 --- a/app/javascript/mastodon/locales/ug.json +++ b/app/javascript/mastodon/locales/ug.json @@ -6,7 +6,6 @@ "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.requested": "Awaiting approval", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account_note.placeholder": "Click to add a note", "column.pins": "Pinned toot", "community.column_settings.media_only": "Media only", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 22cd15bd2..5d7d040fd 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -32,7 +32,7 @@ "account.featured_tags.last_status_never": "Ðемає допиÑів", "account.featured_tags.title": "{name} виділÑÑ” хештеґи", "account.follow": "ПідпиÑатиÑÑ", - "account.follow_back": "ПідпиÑатиÑÑ Ð²Ð·Ð°Ñ”Ð¼Ð½Ð¾", + "account.follow_back": "Стежити також", "account.followers": "ПідпиÑники", "account.followers.empty": "Ðіхто ще не підпиÑаний на цього кориÑтувача.", "account.followers_counter": "{count, plural, one {{counter} підпиÑник} few {{counter} підпиÑники} many {{counter} підпиÑників} other {{counter} підпиÑники}}", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} надÑилає запит на ÑтеженнÑ", "account.share": "ПоділитиÑÑ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÐµÐ¼ @{name}", "account.show_reblogs": "Показати Ð¿Ð¾ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ @{name}", - "account.statuses_counter": "{count, plural, one {{counter} допиÑ} few {{counter} допиÑи} many {{counter} допиÑів} other {{counter} допиÑи}}", + "account.statuses_counter": "{count, plural, one {{counter} допиÑ} few {{counter} допиÑи} many {{counter} допиÑів} other {{counter} допиÑ}}", "account.unblock": "Розблокувати @{name}", "account.unblock_domain": "Розблокувати {domain}", "account.unblock_short": "Розблокувати", @@ -415,6 +415,7 @@ "limited_account_hint.title": "Цей профіль Ñховали модератори {domain}.", "link_preview.author": "Від {name}", "link_preview.more_from_author": "Більше від {name}", + "link_preview.shares": "{count, plural, one {{counter} допиÑ} few {{counter} допиÑи} many {{counter} допиÑів} other {{counter} допиÑ}}", "lists.account.add": "Додати до ÑпиÑку", "lists.account.remove": "Вилучити зі ÑпиÑку", "lists.delete": "Видалити ÑпиÑок", @@ -698,6 +699,7 @@ "server_banner.is_one_of_many": "{domain} - один з багатьох незалежних Ñерверів Mastodon, Ñкі ви можете викориÑтати, щоб брати учаÑÑ‚ÑŒ у федівері.", "server_banner.server_stats": "СтатиÑтика Ñервера:", "sign_in_banner.create_account": "Створити обліковий запиÑ", + "sign_in_banner.follow_anyone": "Слідкуйте за ким завгодно у вÑьому fediverse Ñ– дивітьÑÑ Ð²Ñе це в хронологічному порÑдку. Ðемає алгоритмів, реклами чи наживок Ð´Ð»Ñ Ð½Ð°Ñ‚Ð¸Ñкань при переглÑді.", "sign_in_banner.mastodon_is": "МаÑтодон - найкращий ÑпоÑіб продовжувати Ñвою Ñправу.", "sign_in_banner.sign_in": "Увійти", "sign_in_banner.sso_redirect": "Увійдіть або зареєÑтруйтеÑÑŒ", diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json index 1b9f8d969..cf53eb6fe 100644 --- a/app/javascript/mastodon/locales/ur.json +++ b/app/javascript/mastodon/locales/ur.json @@ -29,9 +29,7 @@ "account.follow_back": "اکاؤنٹ Ú©Ùˆ Ùالو بیک ", "account.followers": "پیروکار", "account.followers.empty": "Ûنوز اس صار٠کی کوئی پیروی Ù†Ûیں کرتا.", - "account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}", "account.following": "Ùالو کر رÛÛ’ Ûیں", - "account.following_counter": "{count, plural, one {{counter} پیروی کر رÛÛ’ Ûیں} other {{counter} پیروی کر رÛÛ’ Ûیں}}", "account.follows.empty": "\"ÛŒÛ ØµØ§Ø±Ù Ûنوز کسی Ú©ÛŒ پیروی Ù†Ûیں کرتا ÛÛ’\".", "account.go_to_profile": "پروÙائل پر جائیں", "account.hide_reblogs": "@{name} سے Ùروغ چھپائیں", @@ -57,7 +55,6 @@ "account.requested_follow": "{name} آپ Ú©Ùˆ Ùالو کرنا چھاتا ÛÛ’Û”", "account.share": "@{name} Ú©Û’ مشخص Ú©Ùˆ بانٹیں", "account.show_reblogs": "@{name} Ú©ÛŒ اÙزائشات Ú©Ùˆ دکھائیں", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "@{name} Ú©Ùˆ بحال کریں", "account.unblock_domain": "{domain} Ú©Ùˆ Ù†Û Ú†Ú¾Ù¾Ø§Ø¦ÛŒÚº", "account.unblock_short": "بلاک ختم کریں", diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json index 77892914a..4824b1d33 100644 --- a/app/javascript/mastodon/locales/uz.json +++ b/app/javascript/mastodon/locales/uz.json @@ -31,9 +31,7 @@ "account.follow": "Obuna bo‘lish", "account.followers": "Obunachilar", "account.followers.empty": "Bu foydalanuvchini hali hech kim kuzatmaydi.", - "account.followers_counter": "{count, plural, one {{counter} Muxlis} other {{counter} Muxlislar}}", "account.following": "Kuzatish", - "account.following_counter": "{count, plural, one {{counter} ga Muxlis} other {{counter} larga muxlis}}", "account.follows.empty": "Bu foydalanuvchi hali hech kimni kuzatmagan.", "account.go_to_profile": "Profilga o'tish", "account.hide_reblogs": "@{name} dan boostlarni yashirish", @@ -54,7 +52,6 @@ "account.requested_follow": "{name} sizni kuzatishni soÊ»radi", "account.share": "@{name} profilini ulashing", "account.show_reblogs": "@{name} dan bootlarni ko'rsatish", - "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Postlar}}", "account.unblock": "@{name} ni blokdan chiqarish", "account.unblock_domain": "{domain} domenini blokdan chiqarish", "account.unblock_short": "Blokdan chiqarish", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 18f0fec3c..70932d10b 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -35,9 +35,9 @@ "account.follow_back": "Theo dõi lại", "account.followers": "NgÆ°á»i theo dõi", "account.followers.empty": "ChÆ°a có ngÆ°á»i theo dõi nào.", - "account.followers_counter": "{count, plural, one {{counter} NgÆ°á»i theo dõi} other {{counter} NgÆ°á»i theo dõi}}", + "account.followers_counter": "{count, plural, other {{counter} ngÆ°á»i theo dõi}}", "account.following": "Äang theo dõi", - "account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}", + "account.following_counter": "{count, plural, other {{counter} Ä‘ang theo dõi}}", "account.follows.empty": "NgÆ°á»i này chÆ°a theo dõi ai.", "account.go_to_profile": "Xem hồ sÆ¡", "account.hide_reblogs": "Ẩn tút @{name} đăng lại", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} yêu cầu theo dõi bạn", "account.share": "Chia sẻ @{name}", "account.show_reblogs": "Hiện tút do @{name} đăng lại", - "account.statuses_counter": "{count, plural, one {{counter} Tút} other {{counter} Tút}}", + "account.statuses_counter": "{count, plural, other {{counter} tút}}", "account.unblock": "Bá» chặn @{name}", "account.unblock_domain": "BỠẩn {domain}", "account.unblock_short": "Bá» chặn", diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json index 1d3a22108..b42bb7589 100644 --- a/app/javascript/mastodon/locales/zgh.json +++ b/app/javascript/mastodon/locales/zgh.json @@ -19,7 +19,6 @@ "account.posts_with_replies": "Toots and replies", "account.requested": "Awaiting approval", "account.share": "ⴱⴹⵓ ⵉⴼⵔⵙ âµ @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unfollow": "ⴽⴽⵙ ⴰⴹⴼⴼⵓⵕ", "account_note.placeholder": "Click to add a note", "bundle_column_error.retry": "â´°âµâµ™ ⴰⵔⵎ", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 3456f99d2..f2accae0d 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -35,9 +35,9 @@ "account.follow_back": "回关", "account.followers": "关注者", "account.followers.empty": "ç›®å‰æ— äººå…³æ³¨æ­¤ç”¨æˆ·ã€‚", - "account.followers_counter": "被 {counter} 人关注", + "account.followers_counter": "{count, plural, other {{counter} 关注者}}", "account.following": "正在关注", - "account.following_counter": "正在关注 {counter} 人", + "account.following_counter": "{count, plural, other {{counter} 关注}}", "account.follows.empty": "此用户目å‰æœªå…³æ³¨ä»»ä½•äººã€‚", "account.go_to_profile": "å‰å¾€ä¸ªäººèµ„料页", "account.hide_reblogs": "éšè—æ¥è‡ª @{name} 的转嘟", @@ -63,7 +63,7 @@ "account.requested_follow": "{name} å·²ç»å‘ä½ å‘é€äº†å…³æ³¨è¯·æ±‚", "account.share": "分享 @{name} 的个人资料页", "account.show_reblogs": "显示æ¥è‡ª @{name} 的转嘟", - "account.statuses_counter": "{counter} æ¡å˜Ÿæ–‡", + "account.statuses_counter": "{count, plural, other {{counter} 嘟文}}", "account.unblock": "å–消å±è”½ @{name}", "account.unblock_domain": "å–消å±è”½ {domain} 域å", "account.unblock_short": "å–消å±è”½", @@ -699,6 +699,7 @@ "server_banner.is_one_of_many": "{domain} 是å¯ç”¨äºŽå‚与è”邦宇宙的众多独立 Mastodon æœåŠ¡å™¨ä¹‹ä¸€ã€‚", "server_banner.server_stats": "æœåŠ¡å™¨ç»Ÿè®¡æ•°æ®ï¼š", "sign_in_banner.create_account": "创建账户", + "sign_in_banner.follow_anyone": "关注è”邦宇宙中的任何人,并按时间顺åºæŸ¥çœ‹æ‰€æœ‰å†…容。没有算法ã€å¹¿å‘Šæˆ–诱导链接。", "sign_in_banner.mastodon_is": "Mastodon 是了解最新动æ€çš„最佳途径。", "sign_in_banner.sign_in": "登录", "sign_in_banner.sso_redirect": "登录或注册", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 5dff46620..09a497e88 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -35,9 +35,7 @@ "account.follow_back": "追蹤å°æ–¹", "account.followers": "追蹤者", "account.followers.empty": "尚未有人追蹤這ä½ä½¿ç”¨è€…。", - "account.followers_counter": "有 {count, plural,one {{counter} 個} other {{counter} 個}}追蹤者", "account.following": "正在追蹤", - "account.following_counter": "正在追蹤 {count, plural,one {{counter}}other {{counter} 人}}", "account.follows.empty": "這ä½ä½¿ç”¨è€…尚未追蹤任何人。", "account.go_to_profile": "å‰å¾€å€‹äººæª”案", "account.hide_reblogs": "éš±è— @{name} 的轉推", @@ -63,7 +61,6 @@ "account.requested_follow": "{name} è¦æ±‚追蹤你", "account.share": "分享 @{name} 的個人檔案", "account.show_reblogs": "顯示 @{name} 的轉推", - "account.statuses_counter": "{count, plural,one {{counter} 篇}other {{counter} 篇}}帖文", "account.unblock": "解除å°éŽ– @{name}", "account.unblock_domain": "解除å°éŽ–網域 {domain}", "account.unblock_short": "解除å°éŽ–", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index e6cd62162..04469a971 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -35,9 +35,9 @@ "account.follow_back": "跟隨回去", "account.followers": "跟隨者", "account.followers.empty": "尚未有人跟隨這ä½ä½¿ç”¨è€…。", - "account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨", + "account.followers_counter": "被 {count, plural, other {{count} 人}}跟隨", "account.following": "跟隨中", - "account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}", + "account.following_counter": "正在跟隨 {count,plural,other {{count} 人}}", "account.follows.empty": "這ä½ä½¿ç”¨è€…尚未跟隨任何人。", "account.go_to_profile": "å‰å¾€å€‹äººæª”案", "account.hide_reblogs": "éš±è—來自 @{name} 的轉嘟", @@ -62,8 +62,8 @@ "account.requested": "正在等候審核。按一下以å–消跟隨請求", "account.requested_follow": "{name} è¦æ±‚跟隨您", "account.share": "分享 @{name} 的個人檔案", - "account.show_reblogs": "顯示來自 @{name} 的嘟文", - "account.statuses_counter": "{count, plural,one {{counter} 則}other {{counter} 則}}嘟文", + "account.show_reblogs": "顯示來自 @{name} 的轉嘟", + "account.statuses_counter": "{count, plural, other {{count} 則嘟文}}", "account.unblock": "解除å°éŽ– @{name}", "account.unblock_domain": "解除å°éŽ–網域 {domain}", "account.unblock_short": "解除å°éŽ–", diff --git a/app/javascript/mastodon/models/status.ts b/app/javascript/mastodon/models/status.ts index 7907fc34f..3900df4e3 100644 --- a/app/javascript/mastodon/models/status.ts +++ b/app/javascript/mastodon/models/status.ts @@ -1,4 +1,12 @@ +import type { RecordOf } from 'immutable'; + +import type { ApiPreviewCardJSON } from 'mastodon/api_types/statuses'; + export type { StatusVisibility } from 'mastodon/api_types/statuses'; // Temporary until we type it correctly export type Status = Immutable.Map; + +type CardShape = Required; + +export type Card = RecordOf; diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index 2f17fed5f..7a4c04c5c 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -1,12 +1,8 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { - DIRECTORY_FETCH_REQUEST, - DIRECTORY_FETCH_SUCCESS, - DIRECTORY_FETCH_FAIL, - DIRECTORY_EXPAND_REQUEST, - DIRECTORY_EXPAND_SUCCESS, - DIRECTORY_EXPAND_FAIL, + expandDirectory, + fetchDirectory } from 'mastodon/actions/directory'; import { FEATURED_TAGS_FETCH_REQUEST, @@ -117,6 +113,7 @@ const normalizeFeaturedTags = (state, path, featuredTags, accountId) => { })); }; +/** @type {import('@reduxjs/toolkit').Reducer} */ export default function userLists(state = initialState, action) { switch(action.type) { case FOLLOWERS_FETCH_SUCCESS: @@ -194,16 +191,6 @@ export default function userLists(state = initialState, action) { case MUTES_FETCH_FAIL: case MUTES_EXPAND_FAIL: return state.setIn(['mutes', 'isLoading'], false); - case DIRECTORY_FETCH_SUCCESS: - return normalizeList(state, ['directory'], action.accounts, action.next); - case DIRECTORY_EXPAND_SUCCESS: - return appendToList(state, ['directory'], action.accounts, action.next); - case DIRECTORY_FETCH_REQUEST: - case DIRECTORY_EXPAND_REQUEST: - return state.setIn(['directory', 'isLoading'], true); - case DIRECTORY_FETCH_FAIL: - case DIRECTORY_EXPAND_FAIL: - return state.setIn(['directory', 'isLoading'], false); case FEATURED_TAGS_FETCH_SUCCESS: return normalizeFeaturedTags(state, ['featured_tags', action.id], action.tags, action.id); case FEATURED_TAGS_FETCH_REQUEST: @@ -211,6 +198,17 @@ export default function userLists(state = initialState, action) { case FEATURED_TAGS_FETCH_FAIL: return state.setIn(['featured_tags', action.id, 'isLoading'], false); default: - return state; + if(fetchDirectory.fulfilled.match(action)) + return normalizeList(state, ['directory'], action.payload.accounts, undefined); + else if( expandDirectory.fulfilled.match(action)) + return appendToList(state, ['directory'], action.payload.accounts, undefined); + else if(fetchDirectory.pending.match(action) || + expandDirectory.pending.match(action)) + return state.setIn(['directory', 'isLoading'], true); + else if(fetchDirectory.rejected.match(action) || + expandDirectory.rejected.match(action)) + return state.setIn(['directory', 'isLoading'], false); + else + return state; } } diff --git a/app/javascript/mastodon/test_helpers.tsx b/app/javascript/mastodon/test_helpers.tsx index 93b5a8453..f40509073 100644 --- a/app/javascript/mastodon/test_helpers.tsx +++ b/app/javascript/mastodon/test_helpers.tsx @@ -2,6 +2,7 @@ import { IntlProvider } from 'react-intl'; import { MemoryRouter } from 'react-router'; +import type { RenderOptions } from '@testing-library/react'; // eslint-disable-next-line import/no-extraneous-dependencies import { render as rtlRender } from '@testing-library/react'; @@ -9,7 +10,11 @@ import { IdentityContext } from './identity_context'; function render( ui: React.ReactElement, - { locale = 'en', signedIn = true, ...renderOptions } = {}, + { + locale = 'en', + signedIn = true, + ...renderOptions + }: RenderOptions & { locale?: string; signedIn?: boolean } = {}, ) { const fakeIdentity = { signedIn: signedIn, diff --git a/app/javascript/styles/mastodon-light/variables.scss b/app/javascript/styles/mastodon-light/variables.scss index 09a75a834..9f571b3f2 100644 --- a/app/javascript/styles/mastodon-light/variables.scss +++ b/app/javascript/styles/mastodon-light/variables.scss @@ -56,11 +56,13 @@ $account-background-color: $white !default; $emojis-requiring-inversion: 'chains'; -.theme-mastodon-light { +body { --dropdown-border-color: #d9e1e8; --dropdown-background-color: #fff; + --modal-border-color: #d9e1e8; + --modal-background-color: var(--background-color-tint); --background-border-color: #d9e1e8; --background-color: #fff; - --background-color-tint: rgba(255, 255, 255, 90%); + --background-color-tint: rgba(255, 255, 255, 80%); --background-filter: blur(10px); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 73d0e6220..c114885d1 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -120,8 +120,27 @@ text-decoration: none; } - &:disabled { - opacity: 0.5; + &.button--destructive { + &:active, + &:focus, + &:hover { + border-color: $ui-button-destructive-focus-background-color; + color: $ui-button-destructive-focus-background-color; + } + } + + &:disabled, + &.disabled { + opacity: 0.7; + border-color: $ui-primary-color; + color: $ui-primary-color; + + &:active, + &:focus, + &:hover { + border-color: $ui-primary-color; + color: $ui-primary-color; + } } } @@ -2420,7 +2439,7 @@ a.account__display-name { } .dropdown-animation { - animation: dropdown 150ms cubic-bezier(0.1, 0.7, 0.1, 1); + animation: dropdown 250ms cubic-bezier(0.1, 0.7, 0.1, 1); @keyframes dropdown { from { @@ -10325,3 +10344,158 @@ noscript { } } } + +.hover-card-controller[data-popper-reference-hidden='true'] { + opacity: 0; + pointer-events: none; +} + +.hover-card { + box-shadow: var(--dropdown-shadow); + background: var(--modal-background-color); + backdrop-filter: var(--background-filter); + border: 1px solid var(--modal-border-color); + border-radius: 8px; + padding: 16px; + width: 270px; + display: flex; + flex-direction: column; + gap: 12px; + + &--loading { + position: relative; + min-height: 100px; + } + + &__name { + display: flex; + gap: 12px; + text-decoration: none; + color: inherit; + } + + &__number { + font-size: 15px; + line-height: 22px; + color: $secondary-text-color; + + strong { + font-weight: 700; + } + } + + &__text-row { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__bio { + color: $secondary-text-color; + font-size: 14px; + line-height: 20px; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + max-height: 2 * 20px; + overflow: hidden; + + p { + margin-bottom: 0; + } + + a { + color: inherit; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + } + + .display-name { + font-size: 15px; + line-height: 22px; + + bdi { + font-weight: 500; + color: $primary-text-color; + } + + &__account { + display: block; + color: $dark-text-color; + } + } + + .account-fields { + color: $secondary-text-color; + font-size: 14px; + line-height: 20px; + + a { + color: inherit; + text-decoration: none; + + &:focus, + &:hover, + &:active { + text-decoration: underline; + } + } + + dl { + display: flex; + align-items: center; + gap: 4px; + + dt { + flex: 0 1 auto; + color: $dark-text-color; + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + dd { + flex: 1 1 auto; + font-weight: 500; + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + text-align: end; + } + + &.verified { + dd { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 4px; + overflow: hidden; + white-space: nowrap; + color: $valid-value-color; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + } + + a { + font-weight: 500; + } + + .icon { + width: 16px; + height: 16px; + } + } + } + } + } +} diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 7ec7e84bd..5d700b496 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -104,7 +104,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def find_existing_status status = status_from_uri(object_uri) status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present? - status + status if status&.account_id == @account.id end def process_status_params diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb index 2fea1057c..d7aaeba5b 100644 --- a/app/lib/application_extension.rb +++ b/app/lib/application_extension.rb @@ -16,7 +16,7 @@ module ApplicationExtension # dependent: delete_all, which means the ActiveRecord callback in # AccessTokenExtension is not run, so instead we manually announce to # streaming that these tokens are being deleted. - before_destroy :push_to_streaming_api, prepend: true + before_destroy :close_streaming_sessions, prepend: true end def confirmation_redirect_uri @@ -29,10 +29,12 @@ module ApplicationExtension redirect_uri.split end - def push_to_streaming_api + def close_streaming_sessions(resource_owner = nil) # TODO: #28793 Combine into a single topic payload = Oj.dump(event: :kill) - access_tokens.in_batches do |tokens| + scope = access_tokens + scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil? + scope.in_batches do |tokens| redis.pipelined do |pipeline| tokens.ids.each do |id| pipeline.publish("timeline:access_token:#{id}", payload) diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index dbfdd33fc..a62ede2bb 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -156,11 +156,11 @@ class LinkDetailsExtractor end def title - html_entities.decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first).strip + html_entities_decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first)&.strip end def description - html_entities.decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description')) + html_entities_decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description')) end def published_at @@ -180,7 +180,7 @@ class LinkDetailsExtractor end def provider_name - html_entities.decode(structured_data&.publisher_name || opengraph_tag('og:site_name')) + html_entities_decode(structured_data&.publisher_name || opengraph_tag('og:site_name')) end def provider_url @@ -188,7 +188,7 @@ class LinkDetailsExtractor end def author_name - html_entities.decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username')) + html_entities_decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username')) end def author_url @@ -257,7 +257,7 @@ class LinkDetailsExtractor next if json_ld.blank? - structured_data = StructuredData.new(html_entities.decode(json_ld)) + structured_data = StructuredData.new(html_entities_decode(json_ld)) next unless structured_data.valid? @@ -273,10 +273,11 @@ class LinkDetailsExtractor end def detect_encoding_and_parse_document - [detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding| + [detect_encoding, nil, @html_charset].uniq.each do |encoding| document = Nokogiri::HTML(@html, nil, encoding) return document if document.to_s.valid_encoding? end + Nokogiri::HTML(@html, nil, 'UTF-8') end def detect_encoding @@ -290,6 +291,15 @@ class LinkDetailsExtractor end end + def html_entities_decode(string) + return if string.nil? + + unicode_string = string.encode('UTF-8') + raise EncodingError, 'cannot convert string to valid UTF-8' unless unicode_string.valid_encoding? + + html_entities.decode(unicode_string) + end + def html_entities @html_entities ||= HTMLEntities.new(:expanded) end diff --git a/app/lib/private_address_check.rb b/app/lib/private_address_check.rb new file mode 100644 index 000000000..d00b16e66 --- /dev/null +++ b/app/lib/private_address_check.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module PrivateAddressCheck + module_function + + CIDR_LIST = [ + IPAddr.new('0.0.0.0/8'), # Current network (only valid as source address) + IPAddr.new('100.64.0.0/10'), # Shared Address Space + IPAddr.new('172.16.0.0/12'), # Private network + IPAddr.new('192.0.0.0/24'), # IETF Protocol Assignments + IPAddr.new('192.0.2.0/24'), # TEST-NET-1, documentation and examples + IPAddr.new('192.88.99.0/24'), # IPv6 to IPv4 relay (includes 2002::/16) + IPAddr.new('198.18.0.0/15'), # Network benchmark tests + IPAddr.new('198.51.100.0/24'), # TEST-NET-2, documentation and examples + IPAddr.new('203.0.113.0/24'), # TEST-NET-3, documentation and examples + IPAddr.new('224.0.0.0/4'), # IP multicast (former Class D network) + IPAddr.new('240.0.0.0/4'), # Reserved (former Class E network) + IPAddr.new('255.255.255.255'), # Broadcast + IPAddr.new('64:ff9b::/96'), # IPv4/IPv6 translation (RFC 6052) + IPAddr.new('100::/64'), # Discard prefix (RFC 6666) + IPAddr.new('2001::/32'), # Teredo tunneling + IPAddr.new('2001:10::/28'), # Deprecated (previously ORCHID) + IPAddr.new('2001:20::/28'), # ORCHIDv2 + IPAddr.new('2001:db8::/32'), # Addresses used in documentation and example source code + IPAddr.new('2002::/16'), # 6to4 + IPAddr.new('fc00::/7'), # Unique local address + IPAddr.new('ff00::/8'), # Multicast + ].freeze + + def private_address?(address) + address.private? || address.loopback? || address.link_local? || CIDR_LIST.any? { |cidr| cidr.include?(address) } + end +end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 927495eac..606819ed4 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -225,7 +225,7 @@ class SearchQueryTransformer < Parslet::Transform end rule(clause: subtree(:clause)) do - prefix = clause[:prefix][:term].to_s if clause[:prefix] + prefix = clause[:prefix][:term].to_s.downcase if clause[:prefix] operator = clause[:operator]&.to_s term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s diff --git a/app/lib/themes.rb b/app/lib/themes.rb index b6da98073..183258d62 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -8,7 +8,7 @@ class Themes THEME_COLORS = { dark: '#191b22', - light: '#f3f5f7', + light: '#ffffff', }.freeze def initialize diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index cbfc39378..eac02ac14 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -128,6 +128,22 @@ class PreviewCard < ApplicationRecord @history ||= Trends::History.new('links', id) end + def authors + @authors ||= [PreviewCard::Author.new(self)] + end + + class Author < ActiveModelSerializers::Model + attributes :name, :url, :account + + def initialize(preview_card) + super( + name: preview_card.author_name, + url: preview_card.author_url, + account: preview_card.author_account, + ) + end + end + class << self private diff --git a/app/models/site_upload.rb b/app/models/site_upload.rb index 6431d1007..273dd6de9 100644 --- a/app/models/site_upload.rb +++ b/app/models/site_upload.rb @@ -31,17 +31,10 @@ class SiteUpload < ApplicationRecord [:"#{size}", { format: 'png', geometry: "#{size}x#{size}#", file_geometry_parser: FastGeometryParser }] end.freeze, - favicon: { - ico: { - format: 'ico', - geometry: '48x48#', - file_geometry_parser: FastGeometryParser, - }.freeze, - }.merge( + favicon: FAVICON_SIZES.to_h do |size| [:"#{size}", { format: 'png', geometry: "#{size}x#{size}#", file_geometry_parser: FastGeometryParser }] - end - ).freeze, + end.freeze, thumbnail: { '@1x': { diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 50dabb91f..089c42fb9 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -42,7 +42,7 @@ class StatusEdit < ApplicationRecord scope :ordered, -> { order(id: :asc) } delegate :local?, :application, :edited?, :edited_at, - :discarded?, :visibility, to: :status + :discarded?, :visibility, :language, to: :status def emojis return @emojis if defined?(@emojis) diff --git a/app/serializers/rest/preview_card_serializer.rb b/app/serializers/rest/preview_card_serializer.rb index 7d4c99c2d..f73a051ac 100644 --- a/app/serializers/rest/preview_card_serializer.rb +++ b/app/serializers/rest/preview_card_serializer.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true class REST::PreviewCardSerializer < ActiveModel::Serializer + class AuthorSerializer < ActiveModel::Serializer + attributes :name, :url + has_one :account, serializer: REST::AccountSerializer + end + include RoutingHelper attributes :url, :title, :description, :language, :type, @@ -8,7 +13,7 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer :provider_url, :html, :width, :height, :image, :image_description, :embed_url, :blurhash, :published_at - has_one :author_account, serializer: REST::AccountSerializer, if: -> { object.author_account.present? } + has_many :authors, serializer: AuthorSerializer def url object.original_url.presence || object.url diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb index b86c9b9e7..dab5f748b 100644 --- a/app/services/account_search_service.rb +++ b/app/services/account_search_service.rb @@ -28,9 +28,7 @@ class AccountSearchService < BaseService }, functions: [ - reputation_score_function, followers_score_function, - time_distance_function, ], }, }, @@ -81,36 +79,12 @@ class AccountSearchService < BaseService } end - # This function deranks accounts that follow more people than follow them - def reputation_score_function - { - script_score: { - script: { - source: "(Math.max(doc['followers_count'].value, 0) + 0.0) / (Math.max(doc['followers_count'].value, 0) + Math.max(doc['following_count'].value, 0) + 1)", - }, - }, - } - end - # This function promotes accounts that have more followers def followers_score_function { script_score: { script: { - source: "(Math.max(doc['followers_count'].value, 0) / (Math.max(doc['followers_count'].value, 0) + 1))", - }, - }, - } - end - - # This function deranks accounts that haven't posted in a long time - def time_distance_function - { - gauss: { - last_status_at: { - scale: '30d', - offset: '30d', - decay: 0.3, + source: "Math.log10((Math.max(doc['followers_count'].value, 0) + 1))", }, }, } @@ -126,10 +100,24 @@ class AccountSearchService < BaseService def core_query { - multi_match: { - query: @query, - type: 'bool_prefix', - fields: %w(username^2 username.*^2 display_name display_name.*), + dis_max: { + queries: [ + { + multi_match: { + query: @query, + type: 'most_fields', + fields: %w(username username.*), + }, + }, + + { + multi_match: { + query: @query, + type: 'most_fields', + fields: %w(display_name display_name.*), + }, + }, + ], }, } end @@ -142,7 +130,7 @@ class AccountSearchService < BaseService { multi_match: { query: @query, - type: 'most_fields', + type: 'best_fields', fields: %w(username^2 display_name^2 text text.*), operator: 'and', }, diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 900cb9863..436e024c9 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -15,6 +15,9 @@ class FetchLinkCardService < BaseService ) }iox + # URL size limit to safely store in PosgreSQL's unique indexes + BYTESIZE_LIMIT = 2692 + def call(status) @status = status @original_url = parse_urls @@ -29,7 +32,7 @@ class FetchLinkCardService < BaseService end attach_card if @card&.persisted? - rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e + rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError => e Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" } nil end @@ -85,7 +88,7 @@ class FetchLinkCardService < BaseService def bad_url?(uri) # Avoid local instance URLs and invalid URLs - uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) + uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) || uri.to_s.bytesize > BYTESIZE_LIMIT end def mention_link?(anchor) diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml index 770d972d9..b03b8ac51 100644 --- a/app/views/admin/statuses/index.html.haml +++ b/app/views/admin/statuses/index.html.haml @@ -8,7 +8,7 @@ %strong= t('admin.statuses.media.title') %ul %li= filter_link_to t('generic.all'), media: nil, id: nil - %li= filter_link_to t('admin.statuses.with_media'), media: '1' + %li= filter_link_to t('admin.statuses.with_media'), media: true .back-link - if params[:report_id] = link_to admin_report_path(params[:report_id].to_i) do diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index e7f1a595e..0c0512e81 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -11,8 +11,6 @@ - if storage_host? %link{ rel: 'dns-prefetch', href: storage_host }/ - %link{ rel: 'icon', href: favicon_path('ico') || '/favicon.ico', type: 'image/x-icon' }/ - - SiteUpload::FAVICON_SIZES.each do |size| %link{ rel: 'icon', sizes: "#{size}x#{size}", href: favicon_path(size.to_i) || frontend_asset_path("icons/favicon-#{size}x#{size}.png"), type: 'image/png' }/ diff --git a/bin/flatware b/bin/flatware new file mode 100755 index 000000000..337ce9277 --- /dev/null +++ b/bin/flatware @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'flatware' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("flatware", "flatware") diff --git a/config/imagemagick/policy.xml b/config/imagemagick/policy.xml index 2730a9f84..e2aa202f2 100644 --- a/config/imagemagick/policy.xml +++ b/config/imagemagick/policy.xml @@ -23,5 +23,5 @@ - + diff --git a/config/initializers/i18n.rb b/config/initializers/i18n.rb index 8643060fa..5e8d3a545 100644 --- a/config/initializers/i18n.rb +++ b/config/initializers/i18n.rb @@ -41,6 +41,7 @@ Rails.application.configure do :hr, :hu, :hy, + :ia, :id, :ie, :ig, diff --git a/config/initializers/statsd.rb b/config/initializers/statsd.rb index a655c1071..f1628a9d1 100644 --- a/config/initializers/statsd.rb +++ b/config/initializers/statsd.rb @@ -3,13 +3,17 @@ if ENV['STATSD_ADDR'].present? host, port = ENV['STATSD_ADDR'].split(':') - statsd = Statsd.new(host, port) - statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') } + begin + statsd = Statsd.new(host, port) + statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') } - NSA.inform_statsd(statsd) do |informant| - informant.collect(:action_controller, :web) - informant.collect(:active_record, :db) - informant.collect(:active_support_cache, :cache) - informant.collect(:sidekiq, :sidekiq) if ENV['STATSD_SIDEKIQ'] == 'true' + NSA.inform_statsd(statsd) do |informant| + informant.collect(:action_controller, :web) + informant.collect(:active_record, :db) + informant.collect(:active_support_cache, :cache) + informant.collect(:sidekiq, :sidekiq) if ENV['STATSD_SIDEKIQ'] == 'true' + end + rescue + Rails.logger.warn("statsd address #{ENV['STATSD_ADDR']} not reachable, proceeding without statsd") end end diff --git a/config/initializers/vapid.rb b/config/initializers/vapid.rb index 7dd870c8b..551ede34f 100644 --- a/config/initializers/vapid.rb +++ b/config/initializers/vapid.rb @@ -5,7 +5,7 @@ Rails.application.configure do # You should only generate this once per instance. If you later decide to change it, all push subscription will # be invalidated, requiring the users to access the website again to resubscribe. # - # Generate with `rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose) + # Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rails mastodon:webpush:generate_vapid_key` if you use docker compose) # # For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html diff --git a/config/initializers/vips.rb b/config/initializers/vips.rb index 25a17b2a1..a539d7035 100644 --- a/config/initializers/vips.rb +++ b/config/initializers/vips.rb @@ -5,7 +5,11 @@ if Rails.configuration.x.use_vips require 'vips' - abort('Incompatible libvips version, please install libvips >= 8.13') unless Vips.at_least_libvips?(8, 13) + unless Vips.at_least_libvips?(8, 13) + abort <<~ERROR.squish + Incompatible libvips version (#{Vips.version_string}), please install libvips >= 8.13 + ERROR + end Vips.block('VipsForeign', true) @@ -25,3 +29,11 @@ if Rails.configuration.x.use_vips Vips.block_untrusted(true) end + +# In some places of the code, we rescue this exception, but we don't always +# load libvips, so it may be an undefined constant: +unless defined?(Vips) + module Vips + class Error < StandardError; end + end +end diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 17c743f1d..20e7e4d46 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -81,7 +81,7 @@ cs: invite_request_text: Důvody založení invited_by: Pozván uživatelem ip: IP adresa - joined: Uživatel založen + joined: Uživatelem od location: all: VÅ¡echny local: Místní @@ -291,6 +291,7 @@ cs: update_custom_emoji_html: Uživatel %{name} aktualizoval emoji %{target} update_domain_block_html: "%{name} aktualizoval blokaci domény %{target}" update_ip_block_html: "%{name} zmÄ›nil pravidlo pro IP %{target}" + update_report_html: "%{name} aktualizoval hlášení %{target}" update_status_html: Uživatel %{name} aktualizoval příspÄ›vek uživatele %{target} update_user_role_html: "%{name} zmÄ›nil %{target} roli" deleted_account: smazaný úÄet @@ -298,6 +299,7 @@ cs: filter_by_action: Filtrovat podle akce filter_by_user: Filtrovat podle uživatele title: Protokol auditu + unavailable_instance: "(doména není k dispozici)" announcements: destroyed_msg: Oznámení bylo úspěšnÄ› odstranÄ›no! edit: @@ -984,6 +986,7 @@ cs: delete: Smazat edit_preset: Upravit pÅ™edlohu pro varování empty: Zatím jste nedefinovali žádné pÅ™edlohy varování. + title: PÅ™edvolby varování webhooks: add_new: PÅ™idat koncový bod delete: Smazat diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml index 332383468..3101779ed 100644 --- a/config/locales/doorkeeper.cs.yml +++ b/config/locales/doorkeeper.cs.yml @@ -166,6 +166,7 @@ cs: admin:write:reports: provádÄ›t moderátorské akce s hlášeními crypto: používat end-to-end Å¡ifrování follow: upravovat vztahy mezi profily + profile: Äíst pouze základní informace o vaÅ¡em úÄtu push: pÅ™ijímat vaÅ¡e push oznámení read: vidÄ›t vÅ¡echna data vaÅ¡eho úÄtu read:accounts: vidÄ›t informace o úÄtech diff --git a/config/locales/doorkeeper.en-GB.yml b/config/locales/doorkeeper.en-GB.yml index b3ceffb13..f254825b1 100644 --- a/config/locales/doorkeeper.en-GB.yml +++ b/config/locales/doorkeeper.en-GB.yml @@ -135,6 +135,7 @@ en-GB: media: Media attachments mutes: Mutes notifications: Notifications + profile: Your Mastodon profile push: Push notifications reports: Reports search: Search @@ -165,6 +166,7 @@ en-GB: admin:write:reports: perform moderation actions on reports crypto: use end-to-end encryption follow: modify account relationships + profile: read only your account's profile information push: receive your push notifications read: read all your account's data read:accounts: see accounts information diff --git a/config/locales/doorkeeper.pt-BR.yml b/config/locales/doorkeeper.pt-BR.yml index d7e9353b5..6b076e908 100644 --- a/config/locales/doorkeeper.pt-BR.yml +++ b/config/locales/doorkeeper.pt-BR.yml @@ -135,6 +135,7 @@ pt-BR: media: Mídias anexadas mutes: Silenciados notifications: Notificações + profile: Seu perfil do Mastodon push: Notificações push reports: Denúncias search: Buscar @@ -165,6 +166,7 @@ pt-BR: admin:write:reports: executar ações de moderação em denúncias crypto: usar criptografia de ponta-a-ponta follow: alterar o relacionamento das contas + profile: ler somente as informações do perfil da sua conta push: receber notificações push read: ler todos os dados da sua conta read:accounts: ver informações das contas diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 07eb84ebb..928823b99 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -285,6 +285,7 @@ en-GB: update_custom_emoji_html: "%{name} updated emoji %{target}" update_domain_block_html: "%{name} updated domain block for %{target}" update_ip_block_html: "%{name} changed rule for IP %{target}" + update_report_html: "%{name} updated report %{target}" update_status_html: "%{name} updated post by %{target}" update_user_role_html: "%{name} changed %{target} role" deleted_account: deleted account @@ -292,6 +293,7 @@ en-GB: filter_by_action: Filter by action filter_by_user: Filter by user title: Audit log + unavailable_instance: "(domain name unavailable)" announcements: destroyed_msg: Announcement successfully deleted! edit: @@ -950,6 +952,7 @@ en-GB: delete: Delete edit_preset: Edit warning preset empty: You haven't defined any warning presets yet. + title: Warning presets webhooks: add_new: Add endpoint delete: Delete diff --git a/config/locales/fi.yml b/config/locales/fi.yml index f108718e5..be87258da 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -293,7 +293,7 @@ fi: filter_by_action: Suodata tapahtuman mukaan filter_by_user: Suodata käyttäjän mukaan title: Auditointiloki - unavailable_instance: "(verkkotunnus ei ole saatavilla)" + unavailable_instance: "(verkkotunnus ei saatavilla)" announcements: destroyed_msg: Tiedote poistettu onnistuneesti! edit: diff --git a/config/locales/gl.yml b/config/locales/gl.yml index ad4744e15..c9f08dcad 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -31,18 +31,18 @@ gl: created_msg: Nota de moderación creada correctamente! destroyed_msg: Nota de moderación eliminada de xeito correcto! accounts: - add_email_domain_block: Bloquear o dominio do email + add_email_domain_block: Bloquear o dominio do enderezo approve: Aprobar approved_msg: Aprobada a solicitude de rexistro de %{username} are_you_sure: Está segura? avatar: Imaxe de perfil by_domain: Dominio change_email: - changed_msg: Email mudado de xeito correcto! - current_email: Email actual - label: Mudar email - new_email: Novo email - submit: Mudar email + changed_msg: Correo cambiado de xeito correcto! + current_email: Enderezo actual + label: Cambiar de enderezo + new_email: Novo enderezo + submit: Cambiar de enderezo title: Mudar email de %{username} change_role: changed_msg: Rol mudado correctamente! @@ -64,10 +64,10 @@ gl: display_name: Nome a amosar domain: Dominio edit: Editar - email: Email - email_status: Estado do email + email: Enderezo de correo + email_status: Estado do correo enable: Activar - enable_sign_in_token_auth: Activar autenticación cun token no email + enable_sign_in_token_auth: Activar autenticación cun token no correo enabled: Activado enabled_msg: Desbloqueada a conta de %{username} followers: Seguidoras @@ -132,7 +132,7 @@ gl: resubscribe: Resubscribir role: Rol search: Procurar - search_same_email_domain: Outras usuarias co mesmo dominio de email + search_same_email_domain: Outras usuarias co mesmo dominio de correo search_same_ip: Outras usuarias co mesmo IP security: Seguridade security_measures: @@ -154,9 +154,9 @@ gl: suspension_irreversible: Elimináronse de xeito irreversible os datos desta conta. Podes reactivar a conta para facela usable novamente pero non recuperará os datos eliminados. suspension_reversible_hint_html: Esta conta foi suspendida, e os datos serán totalmente eliminados o %{date}. Ata entón, a conta pode ser restaurada sen danos. Se desexas eliminar agora mesmo todos os datos da conta, podes facelo aquí embaixo. title: Contas - unblock_email: Desbloquear enderezo de email - unblocked_email_msg: Enderezo de email de %{username} desbloqueado - unconfirmed_email: Email non confirmado + unblock_email: Desbloquear enderezo de correo + unblocked_email_msg: Enderezo de correo de %{username} desbloqueado + unconfirmed_email: Enderezo de correo sen confirmar undo_sensitized: Desmarcar como sensible undo_silenced: Desfacer acalar undo_suspension: Desfacer suspensión @@ -173,12 +173,12 @@ gl: approve_appeal: Aprobar apelación approve_user: Aprobar Usuaria assigned_to_self_report: Asignar denuncia - change_email_user: Editar email da usuaria + change_email_user: Editar correo electrónico da usuaria change_role_user: Cambiar Rol da Usuaria confirm_user: Confirmar usuaria create_account_warning: Crear aviso create_announcement: Crear anuncio - create_canonical_email_block: Crear Bloqueo de email + create_canonical_email_block: Crear Bloqueo de correo electrónico create_custom_emoji: Crear emoticonas personalizadas create_domain_allow: Crear Dominio Permitido create_domain_block: Crear bloquedo do Dominio @@ -188,7 +188,7 @@ gl: create_user_role: Crear Rol demote_user: Degradar usuaria destroy_announcement: Eliminar anuncio - destroy_canonical_email_block: Eliminar Bloqueo de email + destroy_canonical_email_block: Eliminar Bloqueo de correo electrónico destroy_custom_emoji: Eliminar emoticona personalizada destroy_domain_allow: Eliminar Dominio permitido destroy_domain_block: Eliminar bloqueo do Dominio @@ -200,7 +200,7 @@ gl: destroy_user_role: Eliminar Rol disable_2fa_user: Desactivar 2FA disable_custom_emoji: Desactivar emoticona personalizada - disable_sign_in_token_auth_user: Desactivar Autenticación por token no email para Usuaria + disable_sign_in_token_auth_user: Desactivar Autenticación con token no correo para Usuaria disable_user: Desactivar usuaria enable_custom_emoji: Activar emoticona personalizada enable_sign_in_token_auth_user: Activar Autenticación con token no email para Usuaria @@ -211,14 +211,14 @@ gl: reject_user: Rexeitar Usuaria remove_avatar_user: Eliminar avatar reopen_report: Reabrir denuncia - resend_user: Reenviar o email de confirmación + resend_user: Reenviar o correo de confirmación reset_password_user: Restabelecer contrasinal resolve_report: Resolver denuncia sensitive_account: Marca o multimedia da túa conta como sensible silence_account: Silenciar conta suspend_account: Suspender conta unassigned_report: Desasignar denuncia - unblock_email_account: Desbloquear enderezo de email + unblock_email_account: Desbloquear enderezo de correo unsensitive_account: Retira a marca de sensible do multimedia da conta unsilence_account: Deixar de silenciar conta unsuspend_account: Retirar suspensión de conta @@ -660,7 +660,7 @@ gl: delete_data_html: Eliminar o perfil e contidos de @%{acct} para os próximos 30 días a non ser que sexa suspendida nese período preview_preamble_html: "@%{acct} vai recibir un aviso co seguinte contido:" record_strike_html: Anotar un aviso contra @%{acct} para axudarche a xestionar futuros problemas con esta conta - send_email_html: Enviar un email de aviso a @%{acct} + send_email_html: Enviar un correo de aviso a @%{acct} warning_placeholder: Razóns adicionais optativas para a acción de moderación. target_origin: Orixe da conta denunciada title: Denuncias @@ -1060,7 +1060,7 @@ gl: redirect_to_app_html: Ãmoste redirixir á app %{app_name}. Se iso non acontece, proba %{clicking_this_link} ou volve ti manualmente á app. registration_complete: Completouse a creación da conta en %{domain}! welcome_title: Benvida, %{name}! - wrong_email_hint: Se o enderezo de email non é correcto, podes cambialo nos axustes da conta. + wrong_email_hint: Se o enderezo de correo non é correcto, podes cambialo nos axustes da conta. delete_account: Eliminar conta delete_account_html: Se queres eliminar a túa conta, podes facelo aquí. Deberás confirmar a acción. description: diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 4932d42ac..7350ffcee 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -574,7 +574,7 @@ ia: enabled: Activate inbox_url: URL del repetitor pending: Attende le approbation del repetitor - save_and_enable: Salveguardar e activar + save_and_enable: Salvar e activar setup: Crear un connexion con un repetitor signatures_not_enabled: Le repetitores pote non functionar correctemente durante que le modo secur o le modo de federation limitate es activate status: Stato @@ -1276,7 +1276,7 @@ ia: other: "%{count} messages individual celate" title: Filtros new: - save: Salveguardar nove filtro + save: Salvar nove filtro title: Adder nove filtro statuses: back_to_filter: Retro al filtro @@ -1294,14 +1294,14 @@ ia: one: "%{count} elemento correspondente al recerca es seligite." other: Tote le %{count} elementos correspondente al recerca es seligite. cancel: Cancellar - changes_saved_msg: Cambios salveguardate con successo! + changes_saved_msg: Le cambiamentos ha essite salvate! confirm: Confirmar copy: Copiar delete: Deler deselect: Deseliger toto none: Necun order_by: Ordinar per - save_changes: Salvar le cambios + save_changes: Salvar le cambiamentos select_all_matching_items: one: Selige %{count} elemento correspondente a tu recerca. other: Selige %{count} elementos correspondente a tu recerca. diff --git a/config/locales/nn.yml b/config/locales/nn.yml index d82c92c26..2da30e662 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -293,6 +293,7 @@ nn: filter_by_action: Sorter etter handling filter_by_user: Sorter etter brukar title: Revisionslogg + unavailable_instance: "(domenenamn er utilgjengeleg)" announcements: destroyed_msg: Kunngjøringen er slettet! edit: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 8d3b53f77..584a9253b 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -293,6 +293,7 @@ pt-BR: filter_by_action: Filtrar por ação filter_by_user: Filtrar por usuário title: Auditar histórico + unavailable_instance: "(nome de domínio indisponível)" announcements: destroyed_msg: Anúncio excluído! edit: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 6dff92bb6..5f5b3676f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1699,6 +1699,7 @@ ru: import: Импорт import_and_export: Импорт и ÑкÑпорт migrate: ÐœÐ¸Ð³Ñ€Ð°Ñ†Ð¸Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð¾Ð¹ запиÑи + notifications: Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾ Ñлектронной почте preferences: ÐаÑтройки profile: Профиль relationships: ПодпиÑки и подпиÑчики @@ -1706,6 +1707,9 @@ ru: strikes: Ð—Ð°Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ð¸ two_factor_authentication: Подтверждение входа webauthn_authentication: Ключи безопаÑноÑти + severed_relationships: + event_type: + user_domain_block: Ð’Ñ‹ заблокировали %{target_name} statuses: attached: audio: diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index 0b1a34e1b..f8422102f 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -59,7 +59,7 @@ cs: setting_display_media_default: Skrývat média oznaÄená jako citlivá setting_display_media_hide_all: Vždy skrývat média setting_display_media_show_all: Vždy zobrazovat média - setting_use_blurhash: Gradienty jsou založeny na barvách skryté grafiky, ale zakrývají jakékoliv detaily + setting_use_blurhash: Gradienty jsou vytvoÅ™eny na základÄ› barvev skrytých médií, ale zakrývají veÅ¡keré detaily setting_use_pending_items: Aktualizovat Äasovou osu až po kliknutí namísto automatického rolování kanálu username: Pouze písmena, Äíslice a podtržítka whole_word: Je-li klíÄové slovo Äi fráze pouze alfanumerická, bude aplikován pouze, pokud se shoduje s celým slovem diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 0411c45bc..e46ccb873 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -255,7 +255,7 @@ gl: require_invite_text: Pedir unha razón para unirse show_domain_blocks: Amosar dominios bloqueados show_domain_blocks_rationale: Explicar porque están bloqueados os dominios - site_contact_email: Email de contacto + site_contact_email: Correo de contacto site_contact_username: Nome do contacto site_extended_description: Descrición ampla site_short_description: Descrición do servidor diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index c0698c3f7..664082dab 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -81,7 +81,7 @@ ja: backups_retention_period: ユーザーã«ã¯ã€å¾Œã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã™ã‚‹ãŸã‚ã«æŠ•ç¨¿ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–を生æˆã™ã‚‹æ©Ÿèƒ½ãŒã‚ã‚Šã¾ã™ã€‚æ­£ã®å€¤ã«è¨­å®šã™ã‚‹ã¨ã€ã“れらã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã¯æŒ‡å®šã•ã‚ŒãŸæ—¥æ•°å¾Œã«è‡ªå‹•çš„ã«ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‹ã‚‰å‰Šé™¤ã•ã‚Œã¾ã™ã€‚ bootstrap_timeline_accounts: ã“れらã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã€æ–°ã—ã„ユーザーå‘ã‘ã®ãŠã™ã™ã‚ユーザーã®ä¸€ç•ªä¸Šã«ãƒ”ン留ã‚ã•ã‚Œã¾ã™ã€‚ closed_registrations_message: アカウント作æˆã‚’åœæ­¢ã—ã¦ã„る時ã«è¡¨ç¤ºã•ã‚Œã¾ã™ - content_cache_retention_period: ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®ã™ã¹ã¦ã®æŠ•ç¨¿ï¼ˆãƒ–ーストや返信をå«ã‚€ï¼‰ã¯ã€æŒ‡å®šã•ã‚ŒãŸæ—¥æ•°ãŒçµŒéŽã™ã‚‹ã¨ã€ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ã‚„ã‚Šã¨ã‚Šã«é–¢ä¿‚ãªã削除ã•ã‚Œã¾ã™ã€‚ã“ã‚Œã«ã¯ã€ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒ–ックマークやãŠæ°—ã«å…¥ã‚Šã¨ã—ã¦ç™»éŒ²ã—ãŸæŠ•ç¨¿ã‚‚å«ã¾ã‚Œã¾ã™ã€‚ç•°ãªã‚‹ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼é–“ã®éžå…¬é–‹ãªå¤‰èº«ã‚‚失ã‚ã‚Œã€å¾©å…ƒã™ã‚‹ã“ã¨ã¯ä¸å¯èƒ½ã§ã™ã€‚ã“ã®è¨­å®šã®ä½¿ç”¨ã¯ç‰¹åˆ¥ãªç›®çš„ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãŸã‚ã®ã‚‚ã®ã§ã‚ã‚Šã€ä¸€èˆ¬çš„ãªç›®çš„ã®ã‚µãƒ¼ãƒãƒ¼ã§ä½¿ç”¨ã™ã‚‹ã—ãŸå ´åˆã€å¤šãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æœŸå¾…ã‚’è£åˆ‡ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚ + content_cache_retention_period: ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®ã™ã¹ã¦ã®æŠ•ç¨¿ï¼ˆãƒ–ーストや返信をå«ã‚€ï¼‰ã¯ã€æŒ‡å®šã•ã‚ŒãŸæ—¥æ•°ãŒçµŒéŽã™ã‚‹ã¨ã€ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ã‚„ã‚Šã¨ã‚Šã«é–¢ä¿‚ãªã削除ã•ã‚Œã¾ã™ã€‚ã“ã‚Œã«ã¯ã€ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒ–ックマークやãŠæ°—ã«å…¥ã‚Šã¨ã—ã¦ç™»éŒ²ã—ãŸæŠ•ç¨¿ã‚‚å«ã¾ã‚Œã¾ã™ã€‚ç•°ãªã‚‹ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼é–“ã®éžå…¬é–‹ãªè¿”信も失ã‚ã‚Œã€å¾©å…ƒã™ã‚‹ã“ã¨ã¯ä¸å¯èƒ½ã§ã™ã€‚ã“ã®è¨­å®šã®ä½¿ç”¨ã¯ç‰¹åˆ¥ãªç›®çš„ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãŸã‚ã®ã‚‚ã®ã§ã‚ã‚Šã€ä¸€èˆ¬çš„ãªç›®çš„ã®ã‚µãƒ¼ãƒãƒ¼ã§ä½¿ç”¨ã—ãŸå ´åˆã€å¤šãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æœŸå¾…ã‚’è£åˆ‡ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚ custom_css: ウェブ版ã®Mastodonã§ã‚«ã‚¹ã‚¿ãƒ ã‚¹ã‚¿ã‚¤ãƒ«ã‚’é©ç”¨ã§ãã¾ã™ã€‚ favicon: デフォルトã®Mastodonã®ãƒ–ックマークアイコンを独自ã®ã‚¢ã‚¤ã‚³ãƒ³ã§ä¸Šæ›¸ãã—ã¾ã™ã€‚WEBPã€PNGã€GIFã€JPGãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚ mascot: 上級者å‘ã‘Webインターフェースã®ã‚¤ãƒ©ã‚¹ãƒˆã‚’上書ãã—ã¾ã™ã€‚ diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 776e473ee..f6e6d4d2a 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -577,7 +577,7 @@ sr-Latn: relays: add_new: Dodaj novi relej delete: ObriÅ¡i - description_html: "Federalni relej je posredniÄki server koji razmenjuje velike koliÄine javnih truba izmeÄ‘u servera na koji je pretplaćen i na koji objavljuje.Može pomoći malim i srednjim serverima da otkriju sadržaj iz fediversa, koji inaÄe zahteva od lokalnih korisnika da ruÄno pratiti ostale ljude na udaljenim serverima." + description_html: "Federalni relej je posredniÄki server koji razmenjuje velike koliÄine javnih objava izmeÄ‘u servera na koji je pretplaćen i na koji objavljuje.Može pomoći malim i srednjim serverima da otkriju sadržaj iz fediverzuma, koji inaÄe zahteva od lokalnih korisnika da ruÄno pratiti ostale ljude na udaljenim serverima." disable: IskljuÄi disabled: IskljuÄen enable: UkljuÄi diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 365b358d5..9bfefde83 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -577,7 +577,7 @@ sr: relays: add_new: Додај нови релеј delete: Обриши - description_html: "Федерални релеј је поÑреднички Ñервер који размењује велике количине јавних труба између Ñервера на који је претплаћен и на који објављује.Може помоћи малим и Ñредњим Ñерверима да открију Ñадржај из федиверÑа, који иначе захтева од локалних кориÑника да ручно пратити оÑтале људе на удаљеним Ñерверима." + description_html: "Федерални релеј је поÑреднички Ñервер који размењује велике количине јавних објава између Ñервера на који је претплаћен и на који објављује.Може помоћи малим и Ñредњим Ñерверима да открију Ñадржај из федиверзума, који иначе захтева од локалних кориÑника да ручно пратити оÑтале људе на удаљеним Ñерверима." disable: ИÑкључи disabled: ИÑкључен enable: Укључи diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 93de27c0b..1317d5f70 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -525,7 +525,7 @@ zh-TW: total_followed_by_us: 被我們跟隨 total_reported: 關於他們的檢舉報告 total_storage: 多媒體附加檔案 - totals_time_period_hint_html: 以下顯示之總和包å«æ‰€æœ‰æ™‚間的資料。 + totals_time_period_hint_html: 以下顯示之統計包å«æ‰€æœ‰æ™‚間的資料。 unknown_instance: 此伺æœå™¨ç›®å‰æ²’有這個網域的紀錄。 invites: deactivate_all: 全部åœç”¨ diff --git a/config/routes.rb b/config/routes.rb index f4662dd5d..4b3bd4f18 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ Rails.application.routes.draw do /public/remote /conversations /lists/(*any) + /links/(*any) /notifications/(*any) /favourites /bookmarks diff --git a/config/routes/settings.rb b/config/routes/settings.rb index 888fa9ecb..b14606656 100644 --- a/config/routes/settings.rb +++ b/config/routes/settings.rb @@ -37,13 +37,13 @@ namespace :settings do end end - resource :otp_authentication, only: [:show, :create], controller: 'two_factor_authentication/otp_authentication' + scope module: :two_factor_authentication do + resource :otp_authentication, only: [:show, :create], controller: :otp_authentication - resources :webauthn_credentials, only: [:index, :new, :create, :destroy], - path: 'security_keys', - controller: 'two_factor_authentication/webauthn_credentials' do - collection do - get :options + resources :webauthn_credentials, only: [:index, :new, :create, :destroy], path: 'security_keys' do + collection do + get :options + end end end diff --git a/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb b/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb index fff07093c..ebb572bd6 100644 --- a/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb +++ b/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true class MakeUrisNullableInStatuses < ActiveRecord::Migration[4.2] - def change + def up change_column :statuses, :uri, :string, null: true, default: nil end + + def down + raise ActiveRecord::IrreversibleMigration + end end diff --git a/db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb b/db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb index b98fffab8..e7fcb75a4 100644 --- a/db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb +++ b/db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ChangePrimaryKeyToBigintOnStatuses < ActiveRecord::Migration[5.0] - def change + def up change_table(:statuses, bulk: true) do |t| t.change :id, :bigint t.change :reblog_of_id, :bigint @@ -16,4 +16,8 @@ class ChangePrimaryKeyToBigintOnStatuses < ActiveRecord::Migration[5.0] change_column :statuses_tags, :status_id, :bigint change_column :stream_entries, :activity_id, :bigint end + + def down + raise ActiveRecord::IrreversibleMigration + end end diff --git a/db/migrate/20170520145338_change_language_filter_to_opt_out.rb b/db/migrate/20170520145338_change_language_filter_to_opt_out.rb index f0a95819c..0e91c4680 100644 --- a/db/migrate/20170520145338_change_language_filter_to_opt_out.rb +++ b/db/migrate/20170520145338_change_language_filter_to_opt_out.rb @@ -5,7 +5,7 @@ class ChangeLanguageFilterToOptOut < ActiveRecord::Migration[5.0] remove_index :users, :allowed_languages change_table(:users, bulk: true) do |t| - t.remove :allowed_languages + t.remove :allowed_languages, type: :string, array: true, default: [], null: false t.column :filtered_languages, :string, array: true, default: [], null: false end diff --git a/db/migrate/20170609145826_remove_default_language_from_statuses.rb b/db/migrate/20170609145826_remove_default_language_from_statuses.rb index 28b4172a8..122c32228 100644 --- a/db/migrate/20170609145826_remove_default_language_from_statuses.rb +++ b/db/migrate/20170609145826_remove_default_language_from_statuses.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true class RemoveDefaultLanguageFromStatuses < ActiveRecord::Migration[5.1] - def change + def up change_column :statuses, :language, :string, default: nil, null: true end + + def down + raise ActiveRecord::IrreversibleMigration + end end diff --git a/docker-compose.yml b/docker-compose.yml index 7089b0d14..7a6f9be50 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,7 +58,7 @@ services: web: build: . - image: ghcr.io/mastodon/mastodon:v4.2.9 + image: ghcr.io/mastodon/mastodon:v4.2.10 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -79,7 +79,7 @@ services: streaming: build: . - image: ghcr.io/mastodon/mastodon:v4.2.9 + image: ghcr.io/mastodon/mastodon:v4.2.10 restart: always env_file: .env.production command: node ./streaming @@ -97,7 +97,7 @@ services: sidekiq: build: . - image: ghcr.io/mastodon/mastodon:v4.2.9 + image: ghcr.io/mastodon/mastodon:v4.2.10 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 15a8f38f2..735e05cc0 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def default_prerelease - 'alpha.4' + 'alpha.5' end def prerelease diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb index 378af0961..fba32ba4c 100644 --- a/lib/paperclip/color_extractor.rb +++ b/lib/paperclip/color_extractor.rb @@ -116,34 +116,23 @@ module Paperclip # The number of occurrences of a color (r, g, b) is thus encoded in band `b` at pixel position `(r, g)` histogram = image.hist_find_ndim(bins: BINS) - # `histogram.max` returns an array of maxima with their pixel positions, but we don't know in which - # band they are + # With `bandunfold`, we get back to a (BINS*BINS)×BINS 2D image with a single band. + # The number of occurrences of a color (r, g, b) is thus encoded at pixel position `(r * BINS + b, g)` + histogram = histogram.bandunfold + _, colors = histogram.max(size: 10, out_array: true, x_array: true, y_array: true) - colors['out_array'].zip(colors['x_array'], colors['y_array']).map do |v, x, y| - rgb_from_xyv(histogram, x, y, v) - end.flatten.reverse.uniq + colors['x_array'].zip(colors['y_array']).map do |x, y| + rgb_from_hist_xy(x, y) + end.flatten.reverse end # rubocop:disable Naming/MethodParameterName - def rgb_from_xyv(image, x, y, v) - pixel = image.getpoint(x, y) - - # As we only have the first 2 dimensions for this maximum, we - # can't distinguish with different maxima with the same `r` and `g` - # values but different `b` values. - # - # Therefore, we return an array of maxima, which is always non-empty, - # but may contain multiple colors with the same values. - - pixel.filter_map.with_index do |pv, z| - next if pv != v - - r = (x + 0.5) * 256 / BINS - g = (y + 0.5) * 256 / BINS - b = (z + 0.5) * 256 / BINS - ColorDiff::Color::RGB.new(r, g, b) - end + def rgb_from_hist_xy(x, y) + r = ((x / BINS) + 0.5) * 256 / BINS + g = (y + 0.5) * 256 / BINS + b = ((x % BINS) + 0.5) * 256 / BINS + ColorDiff::Color::RGB.new(r, g, b) end def w3c_contrast(color1, color2) diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb index 70efe7c1a..3379823cb 100644 --- a/lib/sanitize_ext/sanitize_config.rb +++ b/lib/sanitize_ext/sanitize_config.rb @@ -65,7 +65,7 @@ class Sanitize end MASTODON_STRICT = freeze_config( - elements: %w(p br span a del pre blockquote code b strong u i em ul ol li), + elements: %w(p br span a del pre blockquote code b strong u i em ul ol li ruby rt rp), attributes: { 'a' => %w(href rel class translate), diff --git a/lib/tasks/branding.rake b/lib/tasks/branding.rake index 608fb3af9..be72454ce 100644 --- a/lib/tasks/branding.rake +++ b/lib/tasks/branding.rake @@ -42,7 +42,6 @@ namespace :branding do output_dest = Rails.root.join('app', 'javascript', 'icons') rsvg_convert = Terrapin::CommandLine.new('rsvg-convert', '-w :size -h :size --keep-aspect-ratio :input -o :output') - convert = Terrapin::CommandLine.new('convert', ':input :output', environment: { 'MAGICK_CONFIGURE_PATH' => nil }) favicon_sizes = [16, 32, 48] apple_icon_sizes = [57, 60, 72, 76, 114, 120, 144, 152, 167, 180, 1024] @@ -56,8 +55,6 @@ namespace :branding do rsvg_convert.run(size: size, input: favicon_source, output: output_path) end - convert.run(input: favicons, output: Rails.public_path.join('favicon.ico')) - apple_icon_sizes.each do |size| rsvg_convert.run(size: size, input: app_icon_source, output: output_dest.join("apple-touch-icon-#{size}x#{size}.png")) end diff --git a/package.json b/package.json index 729482f85..404c4f486 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "webpack-assets-manifest": "^4.0.6", "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^3.3.12", - "webpack-merge": "^5.9.0", + "webpack-merge": "^6.0.0", "wicg-inert": "^3.1.2", "workbox-expiration": "^7.0.0", "workbox-precaching": "^7.0.0", @@ -138,8 +138,9 @@ }, "devDependencies": { "@formatjs/cli": "^6.1.1", + "@testing-library/dom": "^10.2.0", "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^15.0.0", + "@testing-library/react": "^16.0.0", "@types/babel__core": "^7.20.1", "@types/emoji-mart": "^3.0.9", "@types/escape-html": "^1.0.2", @@ -177,8 +178,8 @@ "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.29.0", "eslint-plugin-jsdoc": "^48.0.0", - "eslint-plugin-jsx-a11y": "~6.8.0", - "eslint-plugin-promise": "~6.2.0", + "eslint-plugin-jsx-a11y": "~6.9.0", + "eslint-plugin-promise": "~6.4.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^9.0.11", diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index b09a98bb9..000000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb index 4e8bf9ead..4ab6d109e 100644 --- a/spec/controllers/admin/statuses_controller_spec.rb +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -33,7 +33,7 @@ describe Admin::StatusesController do context 'when filtering by media' do before do - get :index, params: { account_id: account.id, media: '1' } + get :index, params: { account_id: account.id, media: true } end it 'returns http success' do @@ -44,6 +44,11 @@ describe Admin::StatusesController do describe 'GET #show' do before do + status.media_attachments << Fabricate(:media_attachment, type: :image, account: status.account) + status.save! + status.snapshot!(at_time: status.created_at, rate_limit: false) + status.update!(text: 'Hello, this is an edited post') + status.snapshot!(rate_limit: false) get :show, params: { account_id: account.id, id: status.id } end diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb index b46b944d0..3fd9f9499 100644 --- a/spec/controllers/oauth/authorized_applications_controller_spec.rb +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -50,9 +50,11 @@ describe Oauth::AuthorizedApplicationsController do let!(:application) { Fabricate(:application) } let!(:access_token) { Fabricate(:accessible_access_token, application: application, resource_owner_id: user.id) } let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) } + let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) } before do sign_in user, scope: :user + allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub) post :destroy, params: { id: application.id } end @@ -67,5 +69,9 @@ describe Oauth::AuthorizedApplicationsController do it 'removes the web_push_subscription' do expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) end + + it 'sends a session kill payload to the streaming server' do + expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') + end end end diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb index ccbb63491..ce2e0749a 100644 --- a/spec/controllers/settings/applications_controller_spec.rb +++ b/spec/controllers/settings/applications_controller_spec.rb @@ -147,14 +147,22 @@ describe Settings::ApplicationsController do end describe 'destroy' do + let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) } + let!(:access_token) { Fabricate(:accessible_access_token, application: app) } + before do + allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub) post :destroy, params: { id: app.id } end - it 'redirects back to applications page and removes the app' do + it 'redirects back to applications page removes the app' do expect(response).to redirect_to(settings_applications_path) expect(Doorkeeper::Application.find_by(id: app.id)).to be_nil end + + it 'sends a session kill payload to the streaming server' do + expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}') + end end describe 'regenerate' do diff --git a/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt b/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt new file mode 100644 index 000000000..ed8a4716a --- /dev/null +++ b/spec/fixtures/requests/latin1_posing_as_utf8_broken.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 158 +accept-ranges: bytes + + + + + + Tofu á l'orange + + +

Tofu á l'orange

+ + diff --git a/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt b/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt new file mode 100644 index 000000000..a24985832 --- /dev/null +++ b/spec/fixtures/requests/latin1_posing_as_utf8_recoverable.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 158 +accept-ranges: bytes + + + + + + Tofu with orange sauce + + +

Tofu á l'orange

+ + diff --git a/spec/fixtures/requests/page_without_title.txt b/spec/fixtures/requests/page_without_title.txt new file mode 100644 index 000000000..0054aa3b7 --- /dev/null +++ b/spec/fixtures/requests/page_without_title.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +server: nginx +date: Thu, 13 Jun 2024 14:33:13 GMT +content-type: text/html; charset=utf-8 +content-length: 171 +accept-ranges: bytes + + + + + + + +

I am not a valid page

+

Thankfully, browsers do not care

+ + diff --git a/spec/fixtures/requests/redirect_with_utf8_url.txt b/spec/fixtures/requests/redirect_with_utf8_url.txt new file mode 100644 index 000000000..08f99ee2a --- /dev/null +++ b/spec/fixtures/requests/redirect_with_utf8_url.txt @@ -0,0 +1,5 @@ +HTTP/1.1 301 Moved Permanently +server: nginx +date: Thu, 27 Jun 2024 11:04:53 GMT +content-type: text/html; charset=UTF-8 +location: http://example.com/ärgerliche-umlaute.html diff --git a/spec/flatware_helper.rb b/spec/flatware_helper.rb new file mode 100644 index 000000000..57a7c1f56 --- /dev/null +++ b/spec/flatware_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +if defined?(Flatware) + Flatware.configure do |config| + config.after_fork do |test_env_number| + unless ENV.fetch('DISABLE_SIMPLECOV', nil) == 'true' + require 'simplecov' + SimpleCov.at_fork.call(test_env_number) # Combines parallel coverage results + end + end + end +end diff --git a/spec/lib/html_aware_formatter_spec.rb b/spec/lib/html_aware_formatter_spec.rb index a20902d4f..b75ccb06e 100644 --- a/spec/lib/html_aware_formatter_spec.rb +++ b/spec/lib/html_aware_formatter_spec.rb @@ -41,6 +41,14 @@ RSpec.describe HtmlAwareFormatter do expect(subject).to_not include 'status__content__spoiler-link' end end + + context 'when given text containing ruby tags for east-asian languages' do + let(:text) { '明日 (Ashita)' } + + it 'keeps the ruby tags' do + expect(subject).to eq '明日 (Ashita)' + end + end end end end diff --git a/spec/lib/plain_text_formatter_spec.rb b/spec/lib/plain_text_formatter_spec.rb index 80b3c331a..b22f473d0 100644 --- a/spec/lib/plain_text_formatter_spec.rb +++ b/spec/lib/plain_text_formatter_spec.rb @@ -72,6 +72,14 @@ RSpec.describe PlainTextFormatter do expect(subject).to eq 'Lorem ipsum' end end + + context 'when text contains HTML ruby tags' do + let(:status) { Fabricate(:status, account: remote_account, text: '

Lorem 明日 (Ashita) ipsum

') } + + it 'strips the comment' do + expect(subject).to eq 'Lorem 明日 (Ashita) ipsum' + end + end end end end diff --git a/spec/lib/sanitize/config_spec.rb b/spec/lib/sanitize/config_spec.rb index 2d8dc2f63..fe0b272c0 100644 --- a/spec/lib/sanitize/config_spec.rb +++ b/spec/lib/sanitize/config_spec.rb @@ -18,6 +18,10 @@ describe Sanitize::Config do expect(Sanitize.fragment('

Check out:

  1. Foo
  2. Bar
', subject)).to eq '

Check out:

  1. Foo
  2. Bar
' end + it 'keeps ruby tags' do + expect(Sanitize.fragment('

明日 (Ashita)

', subject)).to eq '

明日 (Ashita)

' + end + it 'removes a without href' do expect(Sanitize.fragment('Test', subject)).to eq 'Test' end diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 221645ac5..a8f1ce774 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -210,10 +210,14 @@ RSpec.describe MediaAttachment, :paperclip_processing do expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102) expect(media.thumbnail.present?).to be true - # NOTE: Our libvips and ImageMagick implementations currently have different results - expect(media.file.meta['colors']['background']).to eq(ENV['MASTODON_USE_LIBVIPS'] ? '#268cd9' : '#3088d4') + expect(media.file.meta['colors']['background']).to eq(expected_background_color) expect(media.file_file_name).to_not eq 'boop.ogg' end + + def expected_background_color + # The libvips and ImageMagick implementations produce different results + Rails.configuration.x.use_vips ? '#268cd9' : '#3088d4' + end end describe 'mp3 with large cover art' do diff --git a/spec/requests/account_show_page_spec.rb b/spec/requests/account_show_page_spec.rb index 81e965e6e..830d77860 100644 --- a/spec/requests/account_show_page_spec.rb +++ b/spec/requests/account_show_page_spec.rb @@ -9,7 +9,7 @@ describe 'The account show page' do get '/@alice' - expect(head_link_icons.size).to eq(4) # One general favicon and three with sizes + expect(head_link_icons.size).to eq(3) # Three favicons with sizes expect(head_meta_content('og:title')).to match alice.display_name expect(head_meta_content('og:type')).to eq 'profile' diff --git a/spec/requests/api/v1/scheduled_status_spec.rb b/spec/requests/api/v1/scheduled_status_spec.rb index 49ccde275..f4612410b 100644 --- a/spec/requests/api/v1/scheduled_status_spec.rb +++ b/spec/requests/api/v1/scheduled_status_spec.rb @@ -25,6 +25,17 @@ describe 'Scheduled Statuses' do it_behaves_like 'forbidden for wrong scope', 'write write:statuses' end + context 'with an application token' do + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read:statuses') } + + it 'returns http unprocessable entity' do + get api_v1_scheduled_statuses_path, headers: headers + + expect(response) + .to have_http_status(422) + end + end + context 'with correct scope' do let(:scopes) { 'read:statuses' } diff --git a/spec/requests/api/v1/statuses/translations_spec.rb b/spec/requests/api/v1/statuses/translations_spec.rb index 5b0a99456..e2ab5d0b8 100644 --- a/spec/requests/api/v1/statuses/translations_spec.rb +++ b/spec/requests/api/v1/statuses/translations_spec.rb @@ -8,6 +8,22 @@ describe 'API V1 Statuses Translations' do let(:scopes) { 'read:statuses' } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + context 'with an application token' do + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) } + + describe 'POST /api/v1/statuses/:status_id/translate' do + let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') } + + before do + post "/api/v1/statuses/#{status.id}/translate", headers: headers + end + + it 'returns http unprocessable entity' do + expect(response).to have_http_status(422) + end + end + end + context 'with an oauth token' do describe 'POST /api/v1/statuses/:status_id/translate' do let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') } diff --git a/spec/requests/api/v1/timelines/link_spec.rb b/spec/requests/api/v1/timelines/link_spec.rb index a219c9bcd..e1c914ab8 100644 --- a/spec/requests/api/v1/timelines/link_spec.rb +++ b/spec/requests/api/v1/timelines/link_spec.rb @@ -41,6 +41,8 @@ describe 'Link' do end end + it_behaves_like 'forbidden for wrong scope', 'profile' + context 'when there is no preview card' do let(:preview_card) { nil } @@ -80,13 +82,25 @@ describe 'Link' do Form::AdminSettings.new(timeline_preview: false).save end - context 'when the user is not authenticated' do + it_behaves_like 'forbidden for wrong scope', 'profile' + + context 'without an authentication token' do let(:headers) { {} } - it 'returns http unauthorized' do + it 'returns http unprocessable entity' do subject - expect(response).to have_http_status(401) + expect(response).to have_http_status(422) + end + end + + context 'with an application access token, not bound to a user' do + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) } + + it 'returns http unprocessable entity' do + subject + + expect(response).to have_http_status(422) end end diff --git a/spec/requests/api/v1/timelines/public_spec.rb b/spec/requests/api/v1/timelines/public_spec.rb index 364e48d3d..100f6c1bf 100644 --- a/spec/requests/api/v1/timelines/public_spec.rb +++ b/spec/requests/api/v1/timelines/public_spec.rb @@ -34,6 +34,8 @@ describe 'Public' do context 'when the instance allows public preview' do let(:expected_statuses) { [local_status, remote_status, media_status] } + it_behaves_like 'forbidden for wrong scope', 'profile' + context 'with an authorized user' do it_behaves_like 'a successful request to the public timeline' end @@ -99,13 +101,9 @@ describe 'Public' do Form::AdminSettings.new(timeline_preview: false).save end - context 'with an authenticated user' do - let(:expected_statuses) { [local_status, remote_status, media_status] } + it_behaves_like 'forbidden for wrong scope', 'profile' - it_behaves_like 'a successful request to the public timeline' - end - - context 'with an unauthenticated user' do + context 'without an authentication token' do let(:headers) { {} } it 'returns http unprocessable entity' do @@ -114,6 +112,22 @@ describe 'Public' do expect(response).to have_http_status(422) end end + + context 'with an application access token, not bound to a user' do + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) } + + it 'returns http unprocessable entity' do + subject + + expect(response).to have_http_status(422) + end + end + + context 'with an authenticated user' do + let(:expected_statuses) { [local_status, remote_status, media_status] } + + it_behaves_like 'a successful request to the public timeline' + end end end end diff --git a/spec/requests/api/v1/timelines/tag_spec.rb b/spec/requests/api/v1/timelines/tag_spec.rb index 861134170..5e1415bb1 100644 --- a/spec/requests/api/v1/timelines/tag_spec.rb +++ b/spec/requests/api/v1/timelines/tag_spec.rb @@ -30,6 +30,8 @@ RSpec.describe 'Tag' do let(:params) { {} } let(:hashtag) { 'life' } + it_behaves_like 'forbidden for wrong scope', 'profile' + context 'when given only one hashtag' do let(:expected_statuses) { [life_status] } @@ -93,13 +95,15 @@ RSpec.describe 'Tag' do Form::AdminSettings.new(timeline_preview: false).save end - context 'when the user is not authenticated' do + it_behaves_like 'forbidden for wrong scope', 'profile' + + context 'without an authentication token' do let(:headers) { {} } - it 'returns http unauthorized' do + it 'returns http unprocessable entity' do subject - expect(response).to have_http_status(401) + expect(response).to have_http_status(422) end end diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 239f84fde..b2cd99cea 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -27,6 +27,10 @@ RSpec.describe FetchLinkCardService do stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt')) stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt')) stub_request(:get, 'http://example.com/low_confidence_latin1').to_return(request_fixture('low_confidence_latin1.txt')) + stub_request(:get, 'http://example.com/latin1_posing_as_utf8_broken').to_return(request_fixture('latin1_posing_as_utf8_broken.txt')) + stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt')) + stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) + stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt')) Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache @@ -101,6 +105,22 @@ RSpec.describe FetchLinkCardService do end end + context 'with a redirect URL with faulty encoding' do + let(:status) { Fabricate(:status, text: 'http://example.com/aergerliche-umlaute') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end + + context 'with a page that has no title' do + let(:status) { Fabricate(:status, text: 'http://example.com/page_without_title') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end + context 'with a 404 URL' do let(:status) { Fabricate(:status, text: 'http://example.com/not-found') } @@ -150,10 +170,30 @@ RSpec.describe FetchLinkCardService do end context 'with a URL of a page in ISO-8859-1 encoding, that charlock_holmes cannot detect' do - let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') } + context 'when encoding in http header is correct' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') } - it 'decodes the HTML' do - expect(status.preview_card.title).to eq("Tofu á l'orange") + it 'decodes the HTML' do + expect(status.preview_card.title).to eq("Tofu á l'orange") + end + end + + context 'when encoding in http header is incorrect' do + context 'when encoding problems appear in unrelated tags' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_recoverable') } + + it 'decodes the HTML' do + expect(status.preview_card.title).to eq('Tofu with orange sauce') + end + end + + context 'when encoding problems appear in title tag' do + let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_broken') } + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end end end @@ -193,6 +233,19 @@ RSpec.describe FetchLinkCardService do end end + context 'with an URL too long for PostgreSQL unique indexes' do + let(:url) { "http://example.com/#{'a' * 2674}" } + let(:status) { Fabricate(:status, text: url) } + + it 'does not fetch the URL' do + expect(a_request(:get, url)).to_not have_been_made + end + + it 'does not create a preview card' do + expect(status.preview_card).to be_nil + end + end + context 'with a URL of a page with oEmbed support' do let(:html) { 'Hello world' } let(:status) { Fabricate(:status, text: 'http://example.com/html') } diff --git a/spec/support/javascript_errors.rb b/spec/support/javascript_errors.rb index 28a43b3b8..764528536 100644 --- a/spec/support/javascript_errors.rb +++ b/spec/support/javascript_errors.rb @@ -2,7 +2,14 @@ RSpec.configure do |config| config.after(:each, :js, type: :system) do - errors = page.driver.browser.logs.get(:browser) + # Classes of intermittent ignorable errors + ignored_errors = [ + /Error while trying to use the following icon from the Manifest/, # https://github.com/mastodon/mastodon/pull/30793 + ] + errors = page.driver.browser.logs.get(:browser).reject do |error| + ignored_errors.any? { |pattern| pattern.match(error.message) } + end + if errors.present? aggregate_failures 'javascript errrors' do errors.each do |error| diff --git a/streaming/Dockerfile b/streaming/Dockerfile index 319d5b7fd..d9f7615fa 100644 --- a/streaming/Dockerfile +++ b/streaming/Dockerfile @@ -110,4 +110,4 @@ USER mastodon # Expose default Streaming ports EXPOSE 4000 # Run streaming when started -CMD [ node ./streaming/index.js ] +CMD [ "node", "./streaming/index.js" ] diff --git a/streaming/index.js b/streaming/index.js index 154ecbc02..65a63bb11 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -12,7 +12,7 @@ import { Redis } from 'ioredis'; import { JSDOM } from 'jsdom'; import pg from 'pg'; import pgConnectionString from 'pg-connection-string'; -import WebSocket from 'ws'; +import { WebSocketServer } from 'ws'; import { AuthenticationError, RequestError, extractStatusAndMessage as extractErrorStatusAndMessage } from './errors.js'; import { logger, httpLogger, initializeLogLevel, attachWebsocketHttpLogger, createWebsocketLogger } from './logging.js'; @@ -289,7 +289,7 @@ const CHANNEL_NAMES = [ const startServer = async () => { const pgPool = new pg.Pool(pgConfigFromEnv(process.env)); const server = http.createServer(); - const wss = new WebSocket.Server({ noServer: true }); + const wss = new WebSocketServer({ noServer: true }); // Set the X-Request-Id header on WebSockets: wss.on("headers", function onHeaders(headers, req) { diff --git a/yarn.lock b/yarn.lock index ff4e1110d..4c1a79220 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1502,7 +1502,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.24.7 resolution: "@babel/runtime@npm:7.24.7" dependencies: @@ -1558,69 +1558,69 @@ __metadata: languageName: node linkType: hard -"@csstools/cascade-layer-name-parser@npm:^1.0.11": - version: 1.0.11 - resolution: "@csstools/cascade-layer-name-parser@npm:1.0.11" +"@csstools/cascade-layer-name-parser@npm:^1.0.12": + version: 1.0.12 + resolution: "@csstools/cascade-layer-name-parser@npm:1.0.12" peerDependencies: - "@csstools/css-parser-algorithms": ^2.6.3 - "@csstools/css-tokenizer": ^2.3.1 - checksum: 10c0/52ac8369877c8072ff5c111f656bd87e9a2a4b9e44e48fe005c26faeb6cffd83bfe2f463f4f385a2ae5cfe1f82bbf95d26ddaabca18b66c6b657c4fe1520fb43 + "@csstools/css-parser-algorithms": ^2.7.0 + "@csstools/css-tokenizer": ^2.3.2 + checksum: 10c0/5f92aefcbb3f4b660cf7b0db54f6a4ba21a32fa1b64ea4f050a6370233152d4f561ecf5c8e98ca231e73c16e0d9f75b20b0a65153e18b14957658c81e0f68213 languageName: node linkType: hard -"@csstools/color-helpers@npm:^4.2.0": - version: 4.2.0 - resolution: "@csstools/color-helpers@npm:4.2.0" - checksum: 10c0/3f1feac43c2ef35f38b3b06fe74e0acc130283d7efb6874f6624e45e178c1a7b3c7e39816c7421cddbffc2666430906aa6f0d3dd7c7209db1369c0afd4a29b1b +"@csstools/color-helpers@npm:^4.2.1": + version: 4.2.1 + resolution: "@csstools/color-helpers@npm:4.2.1" + checksum: 10c0/72e11b186ad0f6019a9b4b3752e620fa798c2a40cf47e8cad565dff46e572c9342eb8cf804542d7886344a1e540555d77f20119ace6b2d8a45b6e5ef8a41685c languageName: node linkType: hard -"@csstools/css-calc@npm:^1.2.2": - version: 1.2.2 - resolution: "@csstools/css-calc@npm:1.2.2" +"@csstools/css-calc@npm:^1.2.3": + version: 1.2.3 + resolution: "@csstools/css-calc@npm:1.2.3" peerDependencies: - "@csstools/css-parser-algorithms": ^2.6.3 - "@csstools/css-tokenizer": ^2.3.1 - checksum: 10c0/6032b482764a11c1b882d7502928950ab11760044fa7a2c23ecee802002902f6ea8fca045ee2919302af5a5c399e7baa9f68dff001ac6246ac7fef48fb3f6df7 + "@csstools/css-parser-algorithms": ^2.7.0 + "@csstools/css-tokenizer": ^2.3.2 + checksum: 10c0/fb34767ea9638b837167bcecaf945bcc0c5e8f0d811067c4e8c7a57bc8f0955f61107b1ed5e017b95c54acacc8088473e5497a9986bee95b37ec92999e792871 languageName: node linkType: hard -"@csstools/css-color-parser@npm:^2.0.2": - version: 2.0.2 - resolution: "@csstools/css-color-parser@npm:2.0.2" +"@csstools/css-color-parser@npm:^2.0.3": + version: 2.0.3 + resolution: "@csstools/css-color-parser@npm:2.0.3" dependencies: - "@csstools/color-helpers": "npm:^4.2.0" - "@csstools/css-calc": "npm:^1.2.2" + "@csstools/color-helpers": "npm:^4.2.1" + "@csstools/css-calc": "npm:^1.2.3" peerDependencies: - "@csstools/css-parser-algorithms": ^2.6.3 - "@csstools/css-tokenizer": ^2.3.1 - checksum: 10c0/c5ae4ad78745e425dce56da9f1ab053fb4f7963399735df3303305b32123bed0b2237689c2e7e99da2c62387e3226c12ea85e70e275c4027c7507e4ac929bffa + "@csstools/css-parser-algorithms": ^2.7.0 + "@csstools/css-tokenizer": ^2.3.2 + checksum: 10c0/d8860e6b9c65de4f90d4c21e4d66471fd858434cf63af80f812a900371343b753b86a256627e8bd024cb8903a6a0181d2d9c0c65ab5d78cf29d084a761e2adba languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^2.6.3": - version: 2.6.3 - resolution: "@csstools/css-parser-algorithms@npm:2.6.3" +"@csstools/css-parser-algorithms@npm:^2.6.3, @csstools/css-parser-algorithms@npm:^2.7.0": + version: 2.7.0 + resolution: "@csstools/css-parser-algorithms@npm:2.7.0" peerDependencies: - "@csstools/css-tokenizer": ^2.3.1 - checksum: 10c0/6648fda75a1c08096320fb5c04fd13656a0168de13584d2795547fecfb26c2c7d8b3b1fb79ba7aa758714851e98bfbec20d89e28697f999f41f91133eafe4207 + "@csstools/css-tokenizer": ^2.3.2 + checksum: 10c0/fb84fefdf37c41d170f81b687bf1ee1847a970e51cc1fe3a320e3eaf225383ae9a3c4eb6208b83357dfe18c5114353d780e0c65f05d86d6435e5a9ad9334c834 languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^2.3.1": - version: 2.3.1 - resolution: "@csstools/css-tokenizer@npm:2.3.1" - checksum: 10c0/fed6619fb5108e109d4dd10b0e967035a92793bae8fb84544e1342058b6df4e306d9d075623e2201fe88831b1ada797aea3546a8d12229d2d81cd7a5dfee4444 +"@csstools/css-tokenizer@npm:^2.3.1, @csstools/css-tokenizer@npm:^2.3.2": + version: 2.3.2 + resolution: "@csstools/css-tokenizer@npm:2.3.2" + checksum: 10c0/f7d0d8b3e9e2dcdc6547a387253a09dbbacaaffb5c8718bcd7f15dddeefdd441b73fc5f9fad3f03fabef3b37ec4b62be7ff79caab366427fa90eaf54cd8fc452 languageName: node linkType: hard -"@csstools/media-query-list-parser@npm:^2.1.11": - version: 2.1.11 - resolution: "@csstools/media-query-list-parser@npm:2.1.11" +"@csstools/media-query-list-parser@npm:^2.1.11, @csstools/media-query-list-parser@npm:^2.1.12": + version: 2.1.12 + resolution: "@csstools/media-query-list-parser@npm:2.1.12" peerDependencies: - "@csstools/css-parser-algorithms": ^2.6.3 - "@csstools/css-tokenizer": ^2.3.1 - checksum: 10c0/9bcd99f7d28ae3cdaba73fbbfef571b0393dd4e841f522cc796fe5161744f17e327ba1713dad3c481626fade1357c55890e3d365177abed50e857b69130a9be5 + "@csstools/css-parser-algorithms": ^2.7.0 + "@csstools/css-tokenizer": ^2.3.2 + checksum: 10c0/7395cc710d8f54670c1e7a418a88dcf1ae726316272294ec645f6d79a8e931f5d390ba7ed5d0141d29ad7280cd447b8773143dc7676659413de79228130e1a65 languageName: node linkType: hard @@ -1636,46 +1636,46 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-color-function@npm:^3.0.16": - version: 3.0.16 - resolution: "@csstools/postcss-color-function@npm:3.0.16" +"@csstools/postcss-color-function@npm:^3.0.17": + version: 3.0.17 + resolution: "@csstools/postcss-color-function@npm:3.0.17" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/41756a4601a3f1086290dab6ca92b54e201bd94637b54b439c66a04fd628a14e2a0bd1452ad294d2981e2f4bb306758fa5f44639b1c4332320435050749aa487 + checksum: 10c0/6d347fc9fe65cb897c275c129103576e551b74a7c47a1a4dc8160da2fad7752bf51e3cfbff339f86b39c723efac33643168d2dfaac4d3624d072875d18a65a4b languageName: node linkType: hard -"@csstools/postcss-color-mix-function@npm:^2.0.16": - version: 2.0.16 - resolution: "@csstools/postcss-color-mix-function@npm:2.0.16" +"@csstools/postcss-color-mix-function@npm:^2.0.17": + version: 2.0.17 + resolution: "@csstools/postcss-color-mix-function@npm:2.0.17" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/70cd5b291dd615e20e4475517bf0027c90c433241397a66866f89acedb12cb91f45552a162bdd1000636ec56f7d6a099b65e44fe100fd03228fc65f17cfae285 + checksum: 10c0/ba9a406ebe4caba6709878ee26debb06780be5cbf4e6ab7e902d79ca6e21ec6a8409b9dc0a5ef36fc4bf54bf2bd8f9642b72da8d7939145f99dc40fedd2be9d2 languageName: node linkType: hard -"@csstools/postcss-exponential-functions@npm:^1.0.7": - version: 1.0.7 - resolution: "@csstools/postcss-exponential-functions@npm:1.0.7" +"@csstools/postcss-exponential-functions@npm:^1.0.8": + version: 1.0.8 + resolution: "@csstools/postcss-exponential-functions@npm:1.0.8" dependencies: - "@csstools/css-calc": "npm:^1.2.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-calc": "npm:^1.2.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/2079c81c3437686ef432d88502fa3a13bf8a27b7af105b4c6c2eb8e779f14adc8967a5a3ed03271ab919eeaf999fc4489fe4b37d32a8f61ab3212439517bddcc + checksum: 10c0/6b049801fc1275b34f43ffbb915f447a54cbff7bf48ab0705c3ad1ffde055cb876c4dc24e7a9162cd65e219457328e298a673f6176446493db17cf7af6f90dc0 languageName: node linkType: hard @@ -1691,46 +1691,46 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-gamut-mapping@npm:^1.0.9": - version: 1.0.9 - resolution: "@csstools/postcss-gamut-mapping@npm:1.0.9" +"@csstools/postcss-gamut-mapping@npm:^1.0.10": + version: 1.0.10 + resolution: "@csstools/postcss-gamut-mapping@npm:1.0.10" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/412ae1410f3fce240401576441637c2c4e71d1a54153ac9b7a991b3de7519c253d03e10db78b09872eb10b0776d7f960b442779efabc11332b5be6672163c836 + checksum: 10c0/6c2dab6a84f81904bed89cb584bd9bc6a904b49a4fa315b17be65c7d68baefe592561ee439660d5602b7481bac3be9a93189dc45404764524495400f34c6b6e6 languageName: node linkType: hard -"@csstools/postcss-gradients-interpolation-method@npm:^4.0.17": - version: 4.0.17 - resolution: "@csstools/postcss-gradients-interpolation-method@npm:4.0.17" +"@csstools/postcss-gradients-interpolation-method@npm:^4.0.18": + version: 4.0.18 + resolution: "@csstools/postcss-gradients-interpolation-method@npm:4.0.18" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/465ac42856ca1a57aa2b9ea41ede31d9e2bcf2fe84345dbc182ae41f463069a0cfd41041b834b5133108c702cd85ecb8636b51b0b88fff8a221628639b59f386 + checksum: 10c0/23c431068ac205392b4953dbce411e208e79e221ba8030c5e23c0b82e8fd53bc3bc4f2cdc47050f5d91a4ac69cb80f4f1853b213aa8072fa60a6cb6ff0621e04 languageName: node linkType: hard -"@csstools/postcss-hwb-function@npm:^3.0.15": - version: 3.0.15 - resolution: "@csstools/postcss-hwb-function@npm:3.0.15" +"@csstools/postcss-hwb-function@npm:^3.0.16": + version: 3.0.16 + resolution: "@csstools/postcss-hwb-function@npm:3.0.16" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/fdfaeefbab1008ab1e4a98a2b45cc3db002b2724c404fa0600954b411a68b1fa4028286250bf9898eed10fa80c44e4d6b4e55f1aca073c3dfce8198a0aaedf3f + checksum: 10c0/4deca8831a69038aff719a77df92c53578bb28e23cc61dc4ea7b1d912b1b685683a9c6232396c2616948ac2e8488ad1e2009c9c8ed30c493d97ba8ad37b6418d languageName: node linkType: hard @@ -1768,17 +1768,17 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-light-dark-function@npm:^1.0.5": - version: 1.0.5 - resolution: "@csstools/postcss-light-dark-function@npm:1.0.5" +"@csstools/postcss-light-dark-function@npm:^1.0.6": + version: 1.0.6 + resolution: "@csstools/postcss-light-dark-function@npm:1.0.6" dependencies: - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/4fbeda98372d0da25d3ed87da09903c9a0a5d0b8c13cc9de82a98acce4a8f8367e5ba33bfc25c2534d10f2b1db9d5b4278df4ebab755e27ef2b03a95e0ebe264 + checksum: 10c0/6b2c64860d789cd3e3ce875c01259333911f6e32a751a7475604de8022c13abcb578e5cb941b51bd3a2022bee883df3f6b64800c6e3559b06da283d968aeb615 languageName: node linkType: hard @@ -1820,42 +1820,42 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-logical-viewport-units@npm:^2.0.9": - version: 2.0.9 - resolution: "@csstools/postcss-logical-viewport-units@npm:2.0.9" +"@csstools/postcss-logical-viewport-units@npm:^2.0.10": + version: 2.0.10 + resolution: "@csstools/postcss-logical-viewport-units@npm:2.0.10" dependencies: - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/25b01e36b08c571806d09046be63582dbebf97a4612df59be405fa8a92e6eebcd4e768ad7fbe53b0b8739d6ab04d56957964fb04d6a3ea129fc5f72e6d0adf95 + checksum: 10c0/fe142b11e0e8ccab4667cc5db90b45e67b7d11eaf5c038e91d867e1b18a315ef0859114185aeb48fdc1ce05986be8b644d6157fe9e19da7281f7023c99eb8877 languageName: node linkType: hard -"@csstools/postcss-media-minmax@npm:^1.1.6": - version: 1.1.6 - resolution: "@csstools/postcss-media-minmax@npm:1.1.6" +"@csstools/postcss-media-minmax@npm:^1.1.7": + version: 1.1.7 + resolution: "@csstools/postcss-media-minmax@npm:1.1.7" dependencies: - "@csstools/css-calc": "npm:^1.2.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" - "@csstools/media-query-list-parser": "npm:^2.1.11" + "@csstools/css-calc": "npm:^1.2.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/media-query-list-parser": "npm:^2.1.12" peerDependencies: postcss: ^8.4 - checksum: 10c0/2cbfb3728a232c655d82f63d5ac7da36876d14e5fee5d62a0738efed40c58f20ef11f600395ade24d5063d750e8e093251dd93cc361f782b5a6c0e0f80288f51 + checksum: 10c0/a02943a17b540cbd909b55bbb1f8c9331badc51b613279bbdb7127c9921a5d0675bb41675a3b4d0f15e9586120e5a96d9b9786b63b2c594fbb3a238e860c6ad8 languageName: node linkType: hard -"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^2.0.9": - version: 2.0.9 - resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:2.0.9" +"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^2.0.10": + version: 2.0.10 + resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:2.0.10" dependencies: - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" - "@csstools/media-query-list-parser": "npm:^2.1.11" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/media-query-list-parser": "npm:^2.1.12" peerDependencies: postcss: ^8.4 - checksum: 10c0/d431d2900a7177c938d9dc2d5bdf3c1930758adc214cc72f94b34e6bbd02fd917c200dc81482db515519c97d4f1e766ba3200f3ec9b55081887f2f8111f68e20 + checksum: 10c0/d7879e72df98d9fe2e5d85a64837e7a73c2df1aea8659d65516f0acb070317edd353531882f0bdfd81510703d1da30d6da861052a0bda85fde1f9eab94b1e467 languageName: node linkType: hard @@ -1882,18 +1882,18 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-oklab-function@npm:^3.0.16": - version: 3.0.16 - resolution: "@csstools/postcss-oklab-function@npm:3.0.16" +"@csstools/postcss-oklab-function@npm:^3.0.17": + version: 3.0.17 + resolution: "@csstools/postcss-oklab-function@npm:3.0.17" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/9c67ee5f51116df16ab6baffa1b3c6c7aa93d53b836f421125ae8824075bd3cfaa1a93594466de0ac935c89c4fc8171e80974e1a15bafa23ea864e4cf1f1c1f2 + checksum: 10c0/ff27a4b6fd8490439aa0f3c91ffa2a42a8cf539d7306d9329cef7ca59f28317cee40253f402d19a18c196471fd39a05842d2974d92f1b131dc748074d91ac4ee languageName: node linkType: hard @@ -1908,18 +1908,18 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-relative-color-syntax@npm:^2.0.16": - version: 2.0.16 - resolution: "@csstools/postcss-relative-color-syntax@npm:2.0.16" +"@csstools/postcss-relative-color-syntax@npm:^2.0.17": + version: 2.0.17 + resolution: "@csstools/postcss-relative-color-syntax@npm:2.0.17" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/cdc965706212dcbc03394f55c79a0ad043d1e0174059c4d0d90e4267fe8e6fd9eef7cfed4f5bbc1f8e89c225c1c042ae792e115bba198eb2daae763d65f44679 + checksum: 10c0/46226351b3825323e3496dcee44ff354cd3ccc9241d837659e1311f428f0b4dc878d9bb762cbb8f63243b7af346728ab7a46c311f9dc38bb609147523c698eab languageName: node linkType: hard @@ -1934,41 +1934,41 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-stepped-value-functions@npm:^3.0.8": - version: 3.0.8 - resolution: "@csstools/postcss-stepped-value-functions@npm:3.0.8" +"@csstools/postcss-stepped-value-functions@npm:^3.0.9": + version: 3.0.9 + resolution: "@csstools/postcss-stepped-value-functions@npm:3.0.9" dependencies: - "@csstools/css-calc": "npm:^1.2.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-calc": "npm:^1.2.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/2be66aa769808245137be8ff14308aa17c3a0d75433f6fd6789114966a78c365dbf173d087e7ff5bc80118c75be2ff740baab83ed39fc0671980f6217779956b + checksum: 10c0/bafe80947abc8613903f1f3f1939ece9780696774f15960aef229733e40e483dc2830145426d49c4f6d0b1dabb35f812c8a2dda0d0dcddc930321e36b5c6ca0b languageName: node linkType: hard -"@csstools/postcss-text-decoration-shorthand@npm:^3.0.6": - version: 3.0.6 - resolution: "@csstools/postcss-text-decoration-shorthand@npm:3.0.6" +"@csstools/postcss-text-decoration-shorthand@npm:^3.0.7": + version: 3.0.7 + resolution: "@csstools/postcss-text-decoration-shorthand@npm:3.0.7" dependencies: - "@csstools/color-helpers": "npm:^4.2.0" + "@csstools/color-helpers": "npm:^4.2.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/5abdc4fad1c3f15e9d47c7af3995dec9cdf4e6f87c5857eb2e149764779b8389f4f4b21d11e6f2509c57c554a0dc5c11f68f212acd04bbc47defa15911ac3eb9 + checksum: 10c0/072b9893ca2409aa16e53e84747d7b7e13071ce19738a0800a139bf71b535e439958d9093df2b85f83eee2e0c44bc22a14bf3a39b5a7508bca9e747a12273d02 languageName: node linkType: hard -"@csstools/postcss-trigonometric-functions@npm:^3.0.8": - version: 3.0.8 - resolution: "@csstools/postcss-trigonometric-functions@npm:3.0.8" +"@csstools/postcss-trigonometric-functions@npm:^3.0.9": + version: 3.0.9 + resolution: "@csstools/postcss-trigonometric-functions@npm:3.0.9" dependencies: - "@csstools/css-calc": "npm:^1.2.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-calc": "npm:^1.2.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/aeed8d1026f4a5cb7afafbadd739af84291d5bfcbcdef2f79b77174f003d0cd0c7f9deb3fe0b9377efab37ce9bb17a2499efd4af8211f5ff9eb01b878b0b62b3 + checksum: 10c0/7a439d31a63d35986dab634d9e415f7ce7c32a2d3d382052b5b730a259a12e44c5f1b14e318d79086253e3d5d4f7d942d0e7317d92eb3421dd08824eebec45fb languageName: node linkType: hard @@ -2768,8 +2768,9 @@ __metadata: "@rails/ujs": "npm:7.1.3" "@reduxjs/toolkit": "npm:^2.0.1" "@svgr/webpack": "npm:^5.5.0" + "@testing-library/dom": "npm:^10.2.0" "@testing-library/jest-dom": "npm:^6.0.0" - "@testing-library/react": "npm:^15.0.0" + "@testing-library/react": "npm:^16.0.0" "@types/babel__core": "npm:^7.20.1" "@types/emoji-mart": "npm:^3.0.9" "@types/escape-html": "npm:^1.0.2" @@ -2829,8 +2830,8 @@ __metadata: eslint-plugin-formatjs: "npm:^4.10.1" eslint-plugin-import: "npm:~2.29.0" eslint-plugin-jsdoc: "npm:^48.0.0" - eslint-plugin-jsx-a11y: "npm:~6.8.0" - eslint-plugin-promise: "npm:~6.2.0" + eslint-plugin-jsx-a11y: "npm:~6.9.0" + eslint-plugin-promise: "npm:~6.4.0" eslint-plugin-react: "npm:^7.33.2" eslint-plugin-react-hooks: "npm:^4.6.0" file-loader: "npm:^6.2.0" @@ -2900,7 +2901,7 @@ __metadata: webpack-bundle-analyzer: "npm:^4.8.0" webpack-cli: "npm:^3.3.12" webpack-dev-server: "npm:^3.11.3" - webpack-merge: "npm:^5.9.0" + webpack-merge: "npm:^6.0.0" wicg-inert: "npm:^3.1.2" workbox-expiration: "npm:^7.0.0" workbox-precaching: "npm:^7.0.0" @@ -3035,6 +3036,13 @@ __metadata: languageName: node linkType: hard +"@pkgr/core@npm:^0.1.0": + version: 0.1.1 + resolution: "@pkgr/core@npm:0.1.1" + checksum: 10c0/3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 + languageName: node + linkType: hard + "@polka/url@npm:^1.0.0-next.20": version: 1.0.0-next.21 resolution: "@polka/url@npm:1.0.0-next.21" @@ -3057,8 +3065,8 @@ __metadata: linkType: hard "@reduxjs/toolkit@npm:^2.0.1": - version: 2.2.5 - resolution: "@reduxjs/toolkit@npm:2.2.5" + version: 2.2.6 + resolution: "@reduxjs/toolkit@npm:2.2.6" dependencies: immer: "npm:^10.0.3" redux: "npm:^5.0.1" @@ -3072,7 +3080,7 @@ __metadata: optional: true react-redux: optional: true - checksum: 10c0/be0593bf26852482fb8716b9248531466c6e8782a3114b823ae680fce90267d8c5512a3231cfecc30b17eff81a4604112772b49ad7ca6a3366ddd4f2a838e53c + checksum: 10c0/60af753e6d02c8acd3c5bc843c846d60b19821d93ff9f4415fa7011ebf17a85301ed42132fabc1aaee8523d8f61418b5ba164a11c31ab29937e485842d3744a0 languageName: node linkType: hard @@ -3348,9 +3356,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:^10.0.0": - version: 10.0.0 - resolution: "@testing-library/dom@npm:10.0.0" +"@testing-library/dom@npm:^10.2.0": + version: 10.2.0 + resolution: "@testing-library/dom@npm:10.2.0" dependencies: "@babel/code-frame": "npm:^7.10.4" "@babel/runtime": "npm:^7.12.5" @@ -3360,7 +3368,7 @@ __metadata: dom-accessibility-api: "npm:^0.5.9" lz-string: "npm:^1.5.0" pretty-format: "npm:^27.0.2" - checksum: 10c0/2d12d2a6018a6f1d15e91834180bc068932c699ff1fcbfb80aa21aba519a4f5329c861dfa852e06ee5615bcb92ef2a0f0e755e32684ea3dada63bc34248382ab + checksum: 10c0/de582dfbeb632436547a0ca5851b5a714a4a17f8e96ab3dc4fb4e454eef52c912b648b7cb6e9fdf477f3eeef97e698f3250f0ce50846f39d04677a44169209f2 languageName: node linkType: hard @@ -3397,21 +3405,23 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:^15.0.0": - version: 15.0.7 - resolution: "@testing-library/react@npm:15.0.7" +"@testing-library/react@npm:^16.0.0": + version: 16.0.0 + resolution: "@testing-library/react@npm:16.0.0" dependencies: "@babel/runtime": "npm:^7.12.5" - "@testing-library/dom": "npm:^10.0.0" - "@types/react-dom": "npm:^18.0.0" peerDependencies: + "@testing-library/dom": ^10.0.0 "@types/react": ^18.0.0 + "@types/react-dom": ^18.0.0 react: ^18.0.0 react-dom: ^18.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/ac8ee8968e81949ecb35f7ee34741c2c043f73dd7fee2247d56f6de6a30de4742af94f25264356863974e54387485b46c9448ecf3f6ca41cf4339011c369f2d4 + "@types/react-dom": + optional: true + checksum: 10c0/297f97bf4722dad05f11d9cafd47d387dbdb096fea4b79b876c7466460f0f2e345b55b81b3e37fc81ed8185c528cb53dd8455ca1b6b019b229edf6c796f11c9f languageName: node linkType: hard @@ -3430,9 +3440,9 @@ __metadata: linkType: hard "@types/aria-query@npm:^5.0.1": - version: 5.0.1 - resolution: "@types/aria-query@npm:5.0.1" - checksum: 10c0/bc9e40ce37bd3a1654948778c7829bd55aea1bc5f2cd06fcf6cd650b07bb388995799e9aab6e2d93a6cf55dcba3b85c155f7ba93adefcc7c2e152fc6057061b5 + version: 5.0.4 + resolution: "@types/aria-query@npm:5.0.4" + checksum: 10c0/dc667bc6a3acc7bba2bccf8c23d56cb1f2f4defaa704cfef595437107efaa972d3b3db9ec1d66bc2711bfc35086821edd32c302bffab36f2e79b97f312069f08 languageName: node linkType: hard @@ -3622,11 +3632,11 @@ __metadata: linkType: hard "@types/http-link-header@npm:^1.0.3": - version: 1.0.5 - resolution: "@types/http-link-header@npm:1.0.5" + version: 1.0.7 + resolution: "@types/http-link-header@npm:1.0.7" dependencies: "@types/node": "npm:*" - checksum: 10c0/adeb13381b38c3625478149820772924c154b4a7250dca62c346810a8378f8968fc7f3a9a4f55ec61de5d06083637540f862c8a920f6a710310c9645d19a077d + checksum: 10c0/ffde4514a286ee62fab86bd5164958f5c9ad9d8012eaeb4f5536efa6157e6cf9f93121d5a39bf160c3712554d945300a223a9f8b1f3fb08cc70b73f539767338 languageName: node linkType: hard @@ -3712,9 +3722,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.195": - version: 4.17.5 - resolution: "@types/lodash@npm:4.17.5" - checksum: 10c0/55924803ed853e72261512bd3eaf2c5b16558c3817feb0a3125ef757afe46e54b86f33d1960e40b7606c0ddab91a96f47966bf5e6006b7abfd8994c13b04b19b + version: 4.17.6 + resolution: "@types/lodash@npm:4.17.6" + checksum: 10c0/3b197ac47af9443fee8c4719c5ffde527d7febc018b827d44a6bc2523c728c7adfdd25196fdcfe3eed827993e0c41a917d0da6e78938b18b2be94164789f1117 languageName: node linkType: hard @@ -3829,7 +3839,7 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.4": +"@types/react-dom@npm:^18.2.4": version: 18.3.0 resolution: "@types/react-dom@npm:18.3.0" dependencies: @@ -4117,14 +4127,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^7.0.0": - version: 7.11.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.11.0" + version: 7.14.1 + resolution: "@typescript-eslint/eslint-plugin@npm:7.14.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:7.11.0" - "@typescript-eslint/type-utils": "npm:7.11.0" - "@typescript-eslint/utils": "npm:7.11.0" - "@typescript-eslint/visitor-keys": "npm:7.11.0" + "@typescript-eslint/scope-manager": "npm:7.14.1" + "@typescript-eslint/type-utils": "npm:7.14.1" + "@typescript-eslint/utils": "npm:7.14.1" + "@typescript-eslint/visitor-keys": "npm:7.14.1" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -4135,25 +4145,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/50fedf832e4de9546569106eab1d10716204ceebc5cc7d62299112c881212270d0f7857e3d6452c07db031d40b58cf27c4d1b1a36043e8e700fc3496e377b54a + checksum: 10c0/7c2b9b98a38d78326b0ff7348fe001203eda10817ca7834a7a01f492ae7c2508469bbafaa933208d6459f8ff6685277685983cf6f6843e556a6ab2aa5c05080c languageName: node linkType: hard "@typescript-eslint/parser@npm:^7.0.0": - version: 7.11.0 - resolution: "@typescript-eslint/parser@npm:7.11.0" + version: 7.14.1 + resolution: "@typescript-eslint/parser@npm:7.14.1" dependencies: - "@typescript-eslint/scope-manager": "npm:7.11.0" - "@typescript-eslint/types": "npm:7.11.0" - "@typescript-eslint/typescript-estree": "npm:7.11.0" - "@typescript-eslint/visitor-keys": "npm:7.11.0" + "@typescript-eslint/scope-manager": "npm:7.14.1" + "@typescript-eslint/types": "npm:7.14.1" + "@typescript-eslint/typescript-estree": "npm:7.14.1" + "@typescript-eslint/visitor-keys": "npm:7.14.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/f5d1343fae90ccd91aea8adf194e22ed3eb4b2ea79d03d8a9ca6e7b669a6db306e93138ec64f7020c5b3128619d50304dea1f06043eaff6b015071822cad4972 + checksum: 10c0/db3169d4852685cfb27db741c557f58a3e52104bfacc7621beb7c94ec36ac2a08d4e410ac86745db52f482fbfc87e99fa0a26c1d7a10d37a215cce85e1661f0e languageName: node linkType: hard @@ -4167,22 +4177,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.11.0": - version: 7.11.0 - resolution: "@typescript-eslint/scope-manager@npm:7.11.0" +"@typescript-eslint/scope-manager@npm:7.14.1": + version: 7.14.1 + resolution: "@typescript-eslint/scope-manager@npm:7.14.1" dependencies: - "@typescript-eslint/types": "npm:7.11.0" - "@typescript-eslint/visitor-keys": "npm:7.11.0" - checksum: 10c0/35f9d88f38f2366017b15c9ee752f2605afa8009fa1eaf81c8b2b71fc22ddd2a33fff794a02015c8991a5fa99f315c3d6d76a5957d3fad1ccbb4cd46735c98b5 + "@typescript-eslint/types": "npm:7.14.1" + "@typescript-eslint/visitor-keys": "npm:7.14.1" + checksum: 10c0/f8c05a0d6f8de4cc19b90a4da308817c66e53f36f7ec48f6cc23e93c7399bc418643d8135933aaf5fc013199cbef0e1ea4223f5147db5ca401b239eaf087011e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.11.0": - version: 7.11.0 - resolution: "@typescript-eslint/type-utils@npm:7.11.0" +"@typescript-eslint/type-utils@npm:7.14.1": + version: 7.14.1 + resolution: "@typescript-eslint/type-utils@npm:7.14.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.11.0" - "@typescript-eslint/utils": "npm:7.11.0" + "@typescript-eslint/typescript-estree": "npm:7.14.1" + "@typescript-eslint/utils": "npm:7.14.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependencies: @@ -4190,7 +4200,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/637395cb0f4c424c610e751906a31dcfedcdbd8c479012da6e81f9be6b930f32317bfe170ccb758d93a411b2bd9c4e7e5d18892094466099c6f9c3dceda81a72 + checksum: 10c0/bd1c4a8db6273e24156fb10da2cbeb52b4eb03f819da193d4b6bd5a95db3b5524c6fe00d088308d8855b9ae60a3b82afa3a06e89982a09a8573561da960758fd languageName: node linkType: hard @@ -4201,10 +4211,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:7.11.0, @typescript-eslint/types@npm:^7.2.0": - version: 7.11.0 - resolution: "@typescript-eslint/types@npm:7.11.0" - checksum: 10c0/c5d6c517124017eb44aa180c8ea1fad26ec8e47502f92fd12245ba3141560e69d7f7e35b8aa160ddd5df63a2952af407e2f62cc58b663c86e1f778ffb5b01789 +"@typescript-eslint/types@npm:7.14.1, @typescript-eslint/types@npm:^7.2.0": + version: 7.14.1 + resolution: "@typescript-eslint/types@npm:7.14.1" + checksum: 10c0/5b7bda83c47a9b386482e63447c6b0ed7bd4e82eb43f11a180c6e2f3d2e7a2828f57bcbed82196ad761c49e363cccf4c81a89f1fc976e9f5f0a79dcc928fa2d2 languageName: node linkType: hard @@ -4227,12 +4237,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.11.0": - version: 7.11.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.11.0" +"@typescript-eslint/typescript-estree@npm:7.14.1": + version: 7.14.1 + resolution: "@typescript-eslint/typescript-estree@npm:7.14.1" dependencies: - "@typescript-eslint/types": "npm:7.11.0" - "@typescript-eslint/visitor-keys": "npm:7.11.0" + "@typescript-eslint/types": "npm:7.14.1" + "@typescript-eslint/visitor-keys": "npm:7.14.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -4242,21 +4252,21 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/a4eda43f352d20edebae0c1c221c4fd9de0673a94988cf1ae3f5e4917ef9cdb9ead8d3673ea8dd6e80d9cf3523a47c295be1326a3fae017b277233f4c4b4026b + checksum: 10c0/a8da9bcc4de3334a225424946abd99374de05c42098455419224bc0f46bb1b66115f8bd6ae268461294b90943ed4a407bcd255c0fa60eb76ba4cdc5fc7c20855 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.11.0": - version: 7.11.0 - resolution: "@typescript-eslint/utils@npm:7.11.0" +"@typescript-eslint/utils@npm:7.14.1": + version: 7.14.1 + resolution: "@typescript-eslint/utils@npm:7.14.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:7.11.0" - "@typescript-eslint/types": "npm:7.11.0" - "@typescript-eslint/typescript-estree": "npm:7.11.0" + "@typescript-eslint/scope-manager": "npm:7.14.1" + "@typescript-eslint/types": "npm:7.14.1" + "@typescript-eslint/typescript-estree": "npm:7.14.1" peerDependencies: eslint: ^8.56.0 - checksum: 10c0/539a7ff8b825ad810fc59a80269094748df1a397a42cdbb212c493fc2486711c7d8fd6d75d4cd8a067822b8e6a11f42c50441977d51c183eec47992506d1cdf8 + checksum: 10c0/c7f635a3c2c6c085e1d51a52088e55cad9d7e1257b1f60378e5eeb6eb0871db027d42747e9ef60a2f557cf9dd68b2ce014d488d795db8f771506290b164b0e5a languageName: node linkType: hard @@ -4287,13 +4297,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.11.0": - version: 7.11.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.11.0" +"@typescript-eslint/visitor-keys@npm:7.14.1": + version: 7.14.1 + resolution: "@typescript-eslint/visitor-keys@npm:7.14.1" dependencies: - "@typescript-eslint/types": "npm:7.11.0" + "@typescript-eslint/types": "npm:7.14.1" eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/664e558d9645896484b7ffc9381837f0d52443bf8d121a5586d02d42ca4d17dc35faf526768c4b1beb52c57c43fae555898eb087651eb1c7a3d60f1085effea1 + checksum: 10c0/39ac489990fcfdcee442f27658431a0eb44ccf694f701a45df2a108c47cea9582e0955bff0d449047549149385f72895a5d7e6c1622ece1fe32594b7cecb85f3 languageName: node linkType: hard @@ -4783,7 +4793,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:5.3.0, aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": +"aria-query@npm:5.3.0, aria-query@npm:^5.0.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" dependencies: @@ -4792,6 +4802,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:~5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10c0/edcbc8044c4663d6f88f785e983e6784f98cb62b4ba1e9dd8d61b725d0203e4cfca38d676aee984c31f354103461102a3d583aa4fbe4fd0a89b679744f4e5faf + languageName: node + linkType: hard + "arr-diff@npm:^4.0.0": version: 4.0.0 resolution: "arr-diff@npm:4.0.0" @@ -4813,7 +4832,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "array-buffer-byte-length@npm:1.0.1" dependencies: @@ -4957,16 +4976,16 @@ __metadata: languageName: node linkType: hard -"array.prototype.tosorted@npm:^1.1.3": - version: 1.1.3 - resolution: "array.prototype.tosorted@npm:1.1.3" +"array.prototype.tosorted@npm:^1.1.4": + version: 1.1.4 + resolution: "array.prototype.tosorted@npm:1.1.4" dependencies: - call-bind: "npm:^1.0.5" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.22.3" - es-errors: "npm:^1.1.0" + es-abstract: "npm:^1.23.3" + es-errors: "npm:^1.3.0" es-shim-unscopables: "npm:^1.0.2" - checksum: 10c0/a27e1ca51168ecacf6042901f5ef021e43c8fa04b6c6b6f2a30bac3645cd2b519cecbe0bc45db1b85b843f64dc3207f0268f700b4b9fbdec076d12d432cf0865 + checksum: 10c0/eb3c4c4fc0381b0bf6dba2ea4d48d367c2827a0d4236a5718d97caaccc6b78f11f4cadf090736e86301d295a6aa4967ed45568f92ced51be8cbbacd9ca410943 languageName: node linkType: hard @@ -5132,10 +5151,10 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:=4.7.0": - version: 4.7.0 - resolution: "axe-core@npm:4.7.0" - checksum: 10c0/89ac5712b5932ac7d23398b4cb5ba081c394a086e343acc68ba49c83472706e18e0799804e8388c779dcdacc465377deb29f2714241d3fbb389cf3a6b275c9ba +"axe-core@npm:^4.9.1": + version: 4.9.1 + resolution: "axe-core@npm:4.9.1" + checksum: 10c0/ac9e5a0c6fa115a43ebffc32a1d2189e1ca6431b5a78e88cdcf94a72a25c5964185682edd94fe6bdb1cb4266c0d06301b022866e0e50dcdf6e3cefe556470110 languageName: node linkType: hard @@ -5150,12 +5169,12 @@ __metadata: languageName: node linkType: hard -"axobject-query@npm:^3.2.1": - version: 3.2.1 - resolution: "axobject-query@npm:3.2.1" +"axobject-query@npm:~3.1.1": + version: 3.1.1 + resolution: "axobject-query@npm:3.1.1" dependencies: - dequal: "npm:^2.0.3" - checksum: 10c0/f7debc2012e456139b57d888c223f6d3cb4b61eb104164a85e3d346273dd6ef0bc9a04b6660ca9407704a14a8e05fa6b6eb9d55f44f348c7210de7ffb350c3a7 + deep-equal: "npm:^2.0.5" + checksum: 10c0/fff3175a22fd1f41fceb7ae0cd25f6594a0d7fba28c2335dd904538b80eb4e1040432564a3c643025cd2bb748f68d35aaabffb780b794da97ecfc748810b25ad languageName: node linkType: hard @@ -5643,7 +5662,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.22.2, browserslist@npm:^4.22.3, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": +"browserslist@npm:^4.0.0, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": version: 4.23.1 resolution: "browserslist@npm:4.23.1" dependencies: @@ -6723,16 +6742,16 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-default@npm:^7.0.3": - version: 7.0.3 - resolution: "cssnano-preset-default@npm:7.0.3" +"cssnano-preset-default@npm:^7.0.4": + version: 7.0.4 + resolution: "cssnano-preset-default@npm:7.0.4" dependencies: browserslist: "npm:^4.23.1" css-declaration-sorter: "npm:^7.2.0" cssnano-utils: "npm:^5.0.0" postcss-calc: "npm:^10.0.0" postcss-colormin: "npm:^7.0.1" - postcss-convert-values: "npm:^7.0.1" + postcss-convert-values: "npm:^7.0.2" postcss-discard-comments: "npm:^7.0.1" postcss-discard-duplicates: "npm:^7.0.0" postcss-discard-empty: "npm:^7.0.0" @@ -6759,7 +6778,7 @@ __metadata: postcss-unique-selectors: "npm:^7.0.1" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/ab3e51003efed6542a12d43c10ca693ab26138a1d035697b9be8f07e084e37a78617cbb8028b0a7e7841302ec151f4ecf35cbd763efe291846b62c35ea4c0bb4 + checksum: 10c0/0083821e778bdf7b8aa9589408a01a717be730f73584e7b81756a6fcf87af05b8f17342025e666572a8d573cc30783f2d817b0f7ad63670398bc3135b017ccad languageName: node linkType: hard @@ -6773,14 +6792,14 @@ __metadata: linkType: hard "cssnano@npm:^7.0.0": - version: 7.0.3 - resolution: "cssnano@npm:7.0.3" + version: 7.0.4 + resolution: "cssnano@npm:7.0.4" dependencies: - cssnano-preset-default: "npm:^7.0.3" + cssnano-preset-default: "npm:^7.0.4" lilconfig: "npm:^3.1.2" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/4cbcd1e0ebe0bd83196cc5b16b3a60d3ebc98326c79b2f71df597bb73c8e3ee1f42b89159d7a038acc398251184d648d9dd516f4194e46746f3af6fa74b4aec7 + checksum: 10c0/3939a0b37b11cb4bae92f7916517c7ba21257551f92517b49a640d5df32e855fb7e73321f4be44d2c2de578309c05d711cdcb1976e95607b1b7f92bd4cbd1350 languageName: node linkType: hard @@ -6993,6 +7012,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10c0/a48244f90fa989f63ff5ef0cc6de1e4916b48ea0220a9c89a378561960814794a5800c600254482a2c8fd2e49d6c2e196131dc983976adb024c94a42dfe4949f + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -7575,7 +7620,7 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.2, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": +"es-abstract@npm:^1.17.2, es-abstract@npm:^1.17.5, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": version: 1.23.3 resolution: "es-abstract@npm:1.23.3" dependencies: @@ -7645,14 +7690,31 @@ __metadata: languageName: node linkType: hard -"es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": +"es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 languageName: node linkType: hard -"es-iterator-helpers@npm:^1.0.15, es-iterator-helpers@npm:^1.0.19": +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.1.3" + has-symbols: "npm:^1.0.3" + is-arguments: "npm:^1.1.1" + is-map: "npm:^2.0.2" + is-set: "npm:^2.0.2" + is-string: "npm:^1.0.7" + isarray: "npm:^2.0.5" + stop-iteration-iterator: "npm:^1.0.0" + checksum: 10c0/ebd11effa79851ea75d7f079405f9d0dc185559fd65d986c6afea59a0ff2d46c2ed8675f19f03dce7429d7f6c14ff9aede8d121fbab78d75cfda6a263030bac0 + languageName: node + linkType: hard + +"es-iterator-helpers@npm:^1.0.19": version: 1.0.19 resolution: "es-iterator-helpers@npm:1.0.19" dependencies: @@ -7674,6 +7736,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.5.3": + version: 1.5.4 + resolution: "es-module-lexer@npm:1.5.4" + checksum: 10c0/300a469488c2f22081df1e4c8398c78db92358496e639b0df7f89ac6455462aaf5d8893939087c1a1cbcbf20eed4610c70e0bcb8f3e4b0d80a5d2611c539408c + languageName: node + linkType: hard + "es-object-atoms@npm:^1.0.0": version: 1.0.0 resolution: "es-object-atoms@npm:1.0.0" @@ -7864,8 +7933,8 @@ __metadata: linkType: hard "eslint-plugin-jsdoc@npm:^48.0.0": - version: 48.2.7 - resolution: "eslint-plugin-jsdoc@npm:48.2.7" + version: 48.5.0 + resolution: "eslint-plugin-jsdoc@npm:48.5.0" dependencies: "@es-joy/jsdoccomment": "npm:~0.43.1" are-docs-informative: "npm:^0.0.2" @@ -7873,46 +7942,48 @@ __metadata: debug: "npm:^4.3.4" escape-string-regexp: "npm:^4.0.0" esquery: "npm:^1.5.0" + parse-imports: "npm:^2.1.0" semver: "npm:^7.6.2" spdx-expression-parse: "npm:^4.0.0" + synckit: "npm:^0.9.0" peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10c0/74d0f95b3d880dd4221dbc0b9341266a6cce3b8ca8d3e30032223af3552364643d6b82ad733d9bc06a20f0d640f21e4d8f5a4b00901d1771572625178b8c40c3 + checksum: 10c0/1c5eb83df06cb228e44ad2c9da5b31987347a45b99d9e7a68957d178487a81603ad3c4c7db1ecba7e8a62d7ae20d9de1aec18a8cf2aa0e9169731cec54f78ab7 languageName: node linkType: hard -"eslint-plugin-jsx-a11y@npm:~6.8.0": - version: 6.8.0 - resolution: "eslint-plugin-jsx-a11y@npm:6.8.0" +"eslint-plugin-jsx-a11y@npm:~6.9.0": + version: 6.9.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.9.0" dependencies: - "@babel/runtime": "npm:^7.23.2" - aria-query: "npm:^5.3.0" - array-includes: "npm:^3.1.7" + aria-query: "npm:~5.1.3" + array-includes: "npm:^3.1.8" array.prototype.flatmap: "npm:^1.3.2" ast-types-flow: "npm:^0.0.8" - axe-core: "npm:=4.7.0" - axobject-query: "npm:^3.2.1" + axe-core: "npm:^4.9.1" + axobject-query: "npm:~3.1.1" damerau-levenshtein: "npm:^1.0.8" emoji-regex: "npm:^9.2.2" - es-iterator-helpers: "npm:^1.0.15" - hasown: "npm:^2.0.0" + es-iterator-helpers: "npm:^1.0.19" + hasown: "npm:^2.0.2" jsx-ast-utils: "npm:^3.3.5" language-tags: "npm:^1.0.9" minimatch: "npm:^3.1.2" - object.entries: "npm:^1.1.7" - object.fromentries: "npm:^2.0.7" + object.fromentries: "npm:^2.0.8" + safe-regex-test: "npm:^1.0.3" + string.prototype.includes: "npm:^2.0.0" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 10c0/199b883e526e6f9d7c54cb3f094abc54f11a1ec816db5fb6cae3b938eb0e503acc10ccba91ca7451633a9d0b9abc0ea03601844a8aba5fe88c5e8897c9ac8f49 + checksum: 10c0/72ac719ca90b6149c8f3c708ac5b1177f6757668b6e174d72a78512d4ac10329331b9c666c21e9561237a96a45d7f147f6a5d270dadbb99eb4ee093f127792c3 languageName: node linkType: hard -"eslint-plugin-promise@npm:~6.2.0": - version: 6.2.0 - resolution: "eslint-plugin-promise@npm:6.2.0" +"eslint-plugin-promise@npm:~6.4.0": + version: 6.4.0 + resolution: "eslint-plugin-promise@npm:6.4.0" peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10c0/5f42ee774023c089453ecb792076c64c6d0739ea6e9d6cdc9d6a63da5ba928c776e349d01cc110548f2c67045ec55343136aa7eb8b486e4ab145ac016c06a492 + checksum: 10c0/5d07be976504f92d1d91756b0b0588a4c65e379af2520dd77c8655203085c0ab43e24d4698d1ac4b50926430cd8eb81cd1cc4c3653aae8386c805577bdf57b6c languageName: node linkType: hard @@ -7926,14 +7997,14 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.33.2": - version: 7.34.2 - resolution: "eslint-plugin-react@npm:7.34.2" + version: 7.34.3 + resolution: "eslint-plugin-react@npm:7.34.3" dependencies: array-includes: "npm:^3.1.8" array.prototype.findlast: "npm:^1.2.5" array.prototype.flatmap: "npm:^1.3.2" array.prototype.toreversed: "npm:^1.1.2" - array.prototype.tosorted: "npm:^1.1.3" + array.prototype.tosorted: "npm:^1.1.4" doctrine: "npm:^2.1.0" es-iterator-helpers: "npm:^1.0.19" estraverse: "npm:^5.3.0" @@ -7949,7 +8020,7 @@ __metadata: string.prototype.matchall: "npm:^4.0.11" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 10c0/37dc04424da8626f20a071466e7238d53ed111c53e5e5398d813ac2cf76a2078f00d91f7833fe5b2f0fc98f2688a75b36e78e9ada9f1068705d23c7031094316 + checksum: 10c0/60717e32c9948e2b4ddc53dac7c4b62c68fc7129c3249079191c941c08ebe7d1f4793d65182922d19427c2a6634e05231a7b74ceee34169afdfd0e43d4a43d26 languageName: node linkType: hard @@ -8818,7 +8889,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": +"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" dependencies: @@ -9658,7 +9729,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" dependencies: @@ -9774,7 +9845,7 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.0.4": +"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" dependencies: @@ -9784,7 +9855,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": version: 3.0.4 resolution: "is-array-buffer@npm:3.0.4" dependencies: @@ -10040,10 +10111,10 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.1": - version: 2.0.2 - resolution: "is-map@npm:2.0.2" - checksum: 10c0/119ff9137a37fd131a72fab3f4ab8c9d6a24b0a1ee26b4eff14dc625900d8675a97785eea5f4174265e2006ed076cc24e89f6e57ebd080a48338d914ec9168a5 +"is-map@npm:^2.0.1, is-map@npm:^2.0.2": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc languageName: node linkType: hard @@ -10165,10 +10236,10 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.1": - version: 2.0.2 - resolution: "is-set@npm:2.0.2" - checksum: 10c0/5f8bd1880df8c0004ce694e315e6e1e47a3452014be792880bb274a3b2cdb952fdb60789636ca6e084c7947ca8b7ae03ccaf54c93a7fcfed228af810559e5432 +"is-set@npm:^2.0.1, is-set@npm:^2.0.2": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 languageName: node linkType: hard @@ -12363,13 +12434,13 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1": - version: 1.1.5 - resolution: "object-is@npm:1.1.5" +"object-is@npm:^1.0.1, object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.1.3" - checksum: 10c0/8c263fb03fc28f1ffb54b44b9147235c5e233dc1ca23768e7d2569740b5d860154d7cc29a30220fe28ed6d8008e2422aefdebfe987c103e1c5d190cf02d9d886 + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + checksum: 10c0/506af444c4dce7f8e31f34fc549e2fb8152d6b9c4a30c6e62852badd7f520b579c679af433e7a072f9d78eb7808d230dc12e1cf58da9154dfbf8813099ea0fe0 languageName: node linkType: hard @@ -12401,7 +12472,7 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.7, object.entries@npm:^1.1.8": +"object.entries@npm:^1.1.8": version: 1.1.8 resolution: "object.entries@npm:1.1.8" dependencies: @@ -12705,6 +12776,16 @@ __metadata: languageName: node linkType: hard +"parse-imports@npm:^2.1.0": + version: 2.1.0 + resolution: "parse-imports@npm:2.1.0" + dependencies: + es-module-lexer: "npm:^1.5.3" + slashes: "npm:^3.0.12" + checksum: 10c0/18ef58008868d2d09e472bb540d63efc7cc27f2c33607e5d09c256ece7a30062cac292bda96d820438e94f3dbf558c85e4b084c10d238baa858796794e6cf628 + languageName: node + linkType: hard + "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -13058,14 +13139,14 @@ __metadata: linkType: hard "pino-http@npm:^10.0.0": - version: 10.1.0 - resolution: "pino-http@npm:10.1.0" + version: 10.2.0 + resolution: "pino-http@npm:10.2.0" dependencies: get-caller-file: "npm:^2.0.5" pino: "npm:^9.0.0" pino-std-serializers: "npm:^7.0.0" process-warning: "npm:^3.0.0" - checksum: 10c0/d97691f2ee248b0aca0e49169d0c7ca0d4c604ee57b63ae264a6f9914fc7277cace74686d5088a876f8152a8d5b8211af904b2d24a516728a662de0e9cc79e9f + checksum: 10c0/0b79cd3602531ee5043693e2a3ccf9d955bd93759e80c0b3a458b95b241f36ca8ebc72c8050b395e9d8fcb9581ebc18ecd6b7dc136526bebe924bc5c5079374d languageName: node linkType: hard @@ -13205,18 +13286,18 @@ __metadata: languageName: node linkType: hard -"postcss-color-functional-notation@npm:^6.0.11": - version: 6.0.11 - resolution: "postcss-color-functional-notation@npm:6.0.11" +"postcss-color-functional-notation@npm:^6.0.12": + version: 6.0.12 + resolution: "postcss-color-functional-notation@npm:6.0.12" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/7fd75e6881cf62f536f79dfc0ae1b709ea0b8b84833cce1671372711f6019ab4360c6a17089b654b2d376b87e7f9455b94f0d13b45ab0ab767e547b604709b3d + checksum: 10c0/2e8faecd2609e1b4eb8c1cab21ecca5e746916795df20e6997d66eb61c29fbb01d3e75fef3e0b3e1c181918a2186570441b81779b1fc429d6d8823fbfa164231 languageName: node linkType: hard @@ -13258,58 +13339,58 @@ __metadata: languageName: node linkType: hard -"postcss-convert-values@npm:^7.0.1": - version: 7.0.1 - resolution: "postcss-convert-values@npm:7.0.1" +"postcss-convert-values@npm:^7.0.2": + version: 7.0.2 + resolution: "postcss-convert-values@npm:7.0.2" dependencies: browserslist: "npm:^4.23.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 10c0/612f025f179f0f2ad7365db8c0b423614dcb8e1e4061875a4691a39dede0bca758d1a8f9f5c8b08e12af053e9e884f65ca5626ccc723d5b3f420650d67fe3046 + checksum: 10c0/beb59faf6aae97e6d3c233c5e6ed06cc60d65c49eec576036e3d0da1a831a1e827e3d41f5e81d016440b4f0bdf1406268ae069c4d5b38a6667b310c3da079d22 languageName: node linkType: hard -"postcss-custom-media@npm:^10.0.6": - version: 10.0.6 - resolution: "postcss-custom-media@npm:10.0.6" +"postcss-custom-media@npm:^10.0.7": + version: 10.0.7 + resolution: "postcss-custom-media@npm:10.0.7" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.11" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" - "@csstools/media-query-list-parser": "npm:^2.1.11" + "@csstools/cascade-layer-name-parser": "npm:^1.0.12" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" + "@csstools/media-query-list-parser": "npm:^2.1.12" peerDependencies: postcss: ^8.4 - checksum: 10c0/98a524bc46b780a86094bbe8007f1e577137da5490823631a683d4b3df4a13e40c5e1ab52380275a54f7011abfd98bb597c6293d964c14f9f22ec6cf9d75c550 + checksum: 10c0/4171385ab9370806861dcf7597e53fd6aa1862e77b475c9c565c95bfcc2b950f920f8da26a6dbec42e257388bca97c274635662b5e81fe3905b5e37babe06569 languageName: node linkType: hard -"postcss-custom-properties@npm:^13.3.10": - version: 13.3.10 - resolution: "postcss-custom-properties@npm:13.3.10" +"postcss-custom-properties@npm:^13.3.11": + version: 13.3.11 + resolution: "postcss-custom-properties@npm:13.3.11" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.11" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/cascade-layer-name-parser": "npm:^1.0.12" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/utilities": "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/52688fd0aaadccfdf4a3d86d3a2ab988163e8108088c5e33fc9145d261f75b92b8321c044a8161345abda10df5715d674330309dcc0c17f2980db5515f6a76d6 + checksum: 10c0/4aa95628aa5d5b6df4dfeedbc3891b9666db88d75930cadc14d2fbba0a1b72f4e3cc3d83b5a0c0b8ce44f85b4fda6ebd7fe7792a1abc0a14d7d63b9f170d299c languageName: node linkType: hard -"postcss-custom-selectors@npm:^7.1.10": - version: 7.1.10 - resolution: "postcss-custom-selectors@npm:7.1.10" +"postcss-custom-selectors@npm:^7.1.11": + version: 7.1.11 + resolution: "postcss-custom-selectors@npm:7.1.11" dependencies: - "@csstools/cascade-layer-name-parser": "npm:^1.0.11" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" - postcss-selector-parser: "npm:^6.0.13" + "@csstools/cascade-layer-name-parser": "npm:^1.0.12" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" + postcss-selector-parser: "npm:^6.1.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/11311ae6f306420223c6bf926fb1798738f3aa525a267de204de8e8ee9de467bf63b580d9ad5dbb0fff4bd9266770a3fa7e27a24af08a2e0a4115d0727d1d043 + checksum: 10c0/f37d2e34239e868b35b7970ec97a7a8f657a9f92ed2b221af44f19949f7c3aedcecd0abb5fa1acb120c5ceffdf7a20869338956a37d7bfc37a83d8088f5d3dd2 languageName: node linkType: hard @@ -13427,18 +13508,18 @@ __metadata: languageName: node linkType: hard -"postcss-lab-function@npm:^6.0.16": - version: 6.0.16 - resolution: "postcss-lab-function@npm:6.0.16" +"postcss-lab-function@npm:^6.0.17": + version: 6.0.17 + resolution: "postcss-lab-function@npm:6.0.17" dependencies: - "@csstools/css-color-parser": "npm:^2.0.2" - "@csstools/css-parser-algorithms": "npm:^2.6.3" - "@csstools/css-tokenizer": "npm:^2.3.1" + "@csstools/css-color-parser": "npm:^2.0.3" + "@csstools/css-parser-algorithms": "npm:^2.7.0" + "@csstools/css-tokenizer": "npm:^2.3.2" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" "@csstools/utilities": "npm:^1.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/ba8717cd8a197ec17acaac1b61631cd4403f07bd406b0c92f2e430a55e3f786cd6c338b626c3326e9178a0f3e58ff838ebaded19f480f39197a9cb17349ecdcd + checksum: 10c0/a331f188b02cc8beb315150232b6b58bc5793e8d61585973d352a9b4d370b908ff354ccf9ea1ba20a956fd37ea4ada918ea975c8d4f69e850d26edf0106436e8 languageName: node linkType: hard @@ -13759,52 +13840,52 @@ __metadata: linkType: hard "postcss-preset-env@npm:^9.5.2": - version: 9.5.14 - resolution: "postcss-preset-env@npm:9.5.14" + version: 9.5.15 + resolution: "postcss-preset-env@npm:9.5.15" dependencies: "@csstools/postcss-cascade-layers": "npm:^4.0.6" - "@csstools/postcss-color-function": "npm:^3.0.16" - "@csstools/postcss-color-mix-function": "npm:^2.0.16" - "@csstools/postcss-exponential-functions": "npm:^1.0.7" + "@csstools/postcss-color-function": "npm:^3.0.17" + "@csstools/postcss-color-mix-function": "npm:^2.0.17" + "@csstools/postcss-exponential-functions": "npm:^1.0.8" "@csstools/postcss-font-format-keywords": "npm:^3.0.2" - "@csstools/postcss-gamut-mapping": "npm:^1.0.9" - "@csstools/postcss-gradients-interpolation-method": "npm:^4.0.17" - "@csstools/postcss-hwb-function": "npm:^3.0.15" + "@csstools/postcss-gamut-mapping": "npm:^1.0.10" + "@csstools/postcss-gradients-interpolation-method": "npm:^4.0.18" + "@csstools/postcss-hwb-function": "npm:^3.0.16" "@csstools/postcss-ic-unit": "npm:^3.0.6" "@csstools/postcss-initial": "npm:^1.0.1" "@csstools/postcss-is-pseudo-class": "npm:^4.0.8" - "@csstools/postcss-light-dark-function": "npm:^1.0.5" + "@csstools/postcss-light-dark-function": "npm:^1.0.6" "@csstools/postcss-logical-float-and-clear": "npm:^2.0.1" "@csstools/postcss-logical-overflow": "npm:^1.0.1" "@csstools/postcss-logical-overscroll-behavior": "npm:^1.0.1" "@csstools/postcss-logical-resize": "npm:^2.0.1" - "@csstools/postcss-logical-viewport-units": "npm:^2.0.9" - "@csstools/postcss-media-minmax": "npm:^1.1.6" - "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^2.0.9" + "@csstools/postcss-logical-viewport-units": "npm:^2.0.10" + "@csstools/postcss-media-minmax": "npm:^1.1.7" + "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^2.0.10" "@csstools/postcss-nested-calc": "npm:^3.0.2" "@csstools/postcss-normalize-display-values": "npm:^3.0.2" - "@csstools/postcss-oklab-function": "npm:^3.0.16" + "@csstools/postcss-oklab-function": "npm:^3.0.17" "@csstools/postcss-progressive-custom-properties": "npm:^3.2.0" - "@csstools/postcss-relative-color-syntax": "npm:^2.0.16" + "@csstools/postcss-relative-color-syntax": "npm:^2.0.17" "@csstools/postcss-scope-pseudo-class": "npm:^3.0.1" - "@csstools/postcss-stepped-value-functions": "npm:^3.0.8" - "@csstools/postcss-text-decoration-shorthand": "npm:^3.0.6" - "@csstools/postcss-trigonometric-functions": "npm:^3.0.8" + "@csstools/postcss-stepped-value-functions": "npm:^3.0.9" + "@csstools/postcss-text-decoration-shorthand": "npm:^3.0.7" + "@csstools/postcss-trigonometric-functions": "npm:^3.0.9" "@csstools/postcss-unset-value": "npm:^3.0.1" autoprefixer: "npm:^10.4.19" - browserslist: "npm:^4.22.3" + browserslist: "npm:^4.23.1" css-blank-pseudo: "npm:^6.0.2" css-has-pseudo: "npm:^6.0.5" css-prefers-color-scheme: "npm:^9.0.1" cssdb: "npm:^8.0.0" postcss-attribute-case-insensitive: "npm:^6.0.3" postcss-clamp: "npm:^4.1.0" - postcss-color-functional-notation: "npm:^6.0.11" + postcss-color-functional-notation: "npm:^6.0.12" postcss-color-hex-alpha: "npm:^9.0.4" postcss-color-rebeccapurple: "npm:^9.0.3" - postcss-custom-media: "npm:^10.0.6" - postcss-custom-properties: "npm:^13.3.10" - postcss-custom-selectors: "npm:^7.1.10" + postcss-custom-media: "npm:^10.0.7" + postcss-custom-properties: "npm:^13.3.11" + postcss-custom-selectors: "npm:^7.1.11" postcss-dir-pseudo-class: "npm:^8.0.1" postcss-double-position-gradients: "npm:^5.0.6" postcss-focus-visible: "npm:^9.0.1" @@ -13812,7 +13893,7 @@ __metadata: postcss-font-variant: "npm:^5.0.0" postcss-gap-properties: "npm:^5.0.1" postcss-image-set-function: "npm:^6.0.3" - postcss-lab-function: "npm:^6.0.16" + postcss-lab-function: "npm:^6.0.17" postcss-logical: "npm:^7.0.1" postcss-nesting: "npm:^12.1.5" postcss-opacity-percentage: "npm:^2.0.0" @@ -13824,7 +13905,7 @@ __metadata: postcss-selector-not: "npm:^7.0.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/8e0c8f5c2e7b8385a770c13185986dc50d7a73b10b98c65c2f86bb4cd2860de722caef8172b1676962dafbbc044d6be1955f2a092e951976a30d4ee33b0d7571 + checksum: 10c0/e2ee0b5d7dbaddb82ff6d51b5882120862d6be184973ae3d55642923183ab441d421d5f9810fe02e680a70dbc85b20b1c2eb02c68f167dcaf3ef80a71dd40e78 languageName: node linkType: hard @@ -13948,13 +14029,13 @@ __metadata: linkType: hard "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.38": - version: 8.4.38 - resolution: "postcss@npm:8.4.38" + version: 8.4.39 + resolution: "postcss@npm:8.4.39" dependencies: nanoid: "npm:^3.3.7" - picocolors: "npm:^1.0.0" + picocolors: "npm:^1.0.1" source-map-js: "npm:^1.2.0" - checksum: 10c0/955407b8f70cf0c14acf35dab3615899a2a60a26718a63c848cf3c29f2467b0533991b985a2b994430d890bd7ec2b1963e36352b0774a19143b5f591540f7c06 + checksum: 10c0/16f5ac3c4e32ee76d1582b3c0dcf1a1fdb91334a45ad755eeb881ccc50318fb8d64047de4f1601ac96e30061df203f0f2e2edbdc0bfc49b9c57bc9fb9bedaea3 languageName: node linkType: hard @@ -14099,12 +14180,12 @@ __metadata: linkType: hard "prom-client@npm:^15.0.0": - version: 15.1.2 - resolution: "prom-client@npm:15.1.2" + version: 15.1.3 + resolution: "prom-client@npm:15.1.3" dependencies: "@opentelemetry/api": "npm:^1.4.0" tdigest: "npm:^0.1.1" - checksum: 10c0/a221db148fa64e29dfd4c6cdcaaae14635495a4272b68917e2b44fcfd988bc57027d275b04489ceeea4d0c4d64d058af842c1300966d2c1ffa255f1fa6af1277 + checksum: 10c0/816525572e5799a2d1d45af78512fb47d073c842dc899c446e94d17cfc343d04282a1627c488c7ca1bcd47f766446d3e49365ab7249f6d9c22c7664a5bce7021 languageName: node linkType: hard @@ -14937,7 +15018,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.2.0, regexp.prototype.flags@npm:^1.5.2": +"regexp.prototype.flags@npm:^1.2.0, regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: @@ -15739,6 +15820,13 @@ __metadata: languageName: node linkType: hard +"slashes@npm:^3.0.12": + version: 3.0.12 + resolution: "slashes@npm:3.0.12" + checksum: 10c0/71ca2a1fcd1ab6814b0fdb8cf9c33a3d54321deec2aa8d173510f0086880201446021a9b9e6a18561f7c472b69a2145977c6a8fb9c53a8ff7be31778f203d175 + languageName: node + linkType: hard + "slice-ansi@npm:^4.0.0": version: 4.0.0 resolution: "slice-ansi@npm:4.0.0" @@ -16148,6 +16236,15 @@ __metadata: languageName: node linkType: hard +"stop-iteration-iterator@npm:^1.0.0": + version: 1.0.0 + resolution: "stop-iteration-iterator@npm:1.0.0" + dependencies: + internal-slot: "npm:^1.0.4" + checksum: 10c0/c4158d6188aac510d9e92925b58709207bd94699e9c31186a040c80932a687f84a51356b5895e6dc72710aad83addb9411c22171832c9ae0e6e11b7d61b0dfb9 + languageName: node + linkType: hard + "stream-browserify@npm:^2.0.1": version: 2.0.2 resolution: "stream-browserify@npm:2.0.2" @@ -16232,6 +16329,16 @@ __metadata: languageName: node linkType: hard +"string.prototype.includes@npm:^2.0.0": + version: 2.0.0 + resolution: "string.prototype.includes@npm:2.0.0" + dependencies: + define-properties: "npm:^1.1.3" + es-abstract: "npm:^1.17.5" + checksum: 10c0/32dff118c9e9dcc87e240b05462fa8ee7248d9e335c0015c1442fe18152261508a2146d9bb87ddae56abab69148a83c61dfaea33f53853812a6a2db737689ed2 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.11, string.prototype.matchall@npm:^4.0.6": version: 4.0.11 resolution: "string.prototype.matchall@npm:4.0.11" @@ -16675,6 +16782,16 @@ __metadata: languageName: node linkType: hard +"synckit@npm:^0.9.0": + version: 0.9.0 + resolution: "synckit@npm:0.9.0" + dependencies: + "@pkgr/core": "npm:^0.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b5c1e7c03fefe3d36a9ab4e71dd21859cb32be4138712c31a893382a568fd00efc59ede8f521dd7e53d43a2fea92bdf717e987ea9ed6ad94f97ef28d71d0ba2f + languageName: node + linkType: hard + "table@npm:^6.8.2": version: 6.8.2 resolution: "table@npm:6.8.2" @@ -17011,13 +17128,20 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2, tslib@npm:^2.4.0": +"tslib@npm:2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb languageName: node linkType: hard +"tslib@npm:^2.4.0, tslib@npm:^2.6.2": + version: 2.6.3 + resolution: "tslib@npm:2.6.3" + checksum: 10c0/2598aef53d9dbe711af75522464b2104724d6467b26a60f2bdac8297d2b5f1f6b86a71f61717384aa8fd897240467aaa7bcc36a0700a0faf751293d1331db39a + languageName: node + linkType: hard + "tty-browserify@npm:0.0.0": version: 0.0.0 resolution: "tty-browserify@npm:0.0.0" @@ -17151,22 +17275,22 @@ __metadata: linkType: hard "typescript@npm:5, typescript@npm:^5.0.4": - version: 5.4.5 - resolution: "typescript@npm:5.4.5" + version: 5.5.2 + resolution: "typescript@npm:5.5.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/2954022ada340fd3d6a9e2b8e534f65d57c92d5f3989a263754a78aba549f7e6529acc1921913560a4b816c46dce7df4a4d29f9f11a3dc0d4213bb76d043251e + checksum: 10c0/8ca39b27b5f9bd7f32db795045933ab5247897660627251e8254180b792a395bf061ea7231947d5d7ffa5cb4cc771970fd4ef543275f9b559f08c9325cccfce3 languageName: node linkType: hard "typescript@patch:typescript@npm%3A5#optional!builtin, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin": - version: 5.4.5 - resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c" + version: 5.5.2 + resolution: "typescript@patch:typescript@npm%3A5.5.2#optional!builtin::version=5.5.2&hash=379a07" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/db2ad2a16ca829f50427eeb1da155e7a45e598eec7b086d8b4e8ba44e5a235f758e606d681c66992230d3fc3b8995865e5fd0b22a2c95486d0b3200f83072ec9 + checksum: 10c0/a7b7ede75dc7fc32a76d0d0af6b91f5fbd8620890d84c906f663d8783bf3de6d7bd50f0430b8bb55eac88a38934af847ff709e7156e5138b95ae94cbd5f73e5b languageName: node linkType: hard @@ -17804,14 +17928,14 @@ __metadata: languageName: node linkType: hard -"webpack-merge@npm:^5.9.0": - version: 5.10.0 - resolution: "webpack-merge@npm:5.10.0" +"webpack-merge@npm:^6.0.0": + version: 6.0.1 + resolution: "webpack-merge@npm:6.0.1" dependencies: clone-deep: "npm:^4.0.1" flat: "npm:^5.0.2" - wildcard: "npm:^2.0.0" - checksum: 10c0/b607c84cabaf74689f965420051a55a08722d897bdd6c29cb0b2263b451c090f962d41ecf8c9bf56b0ab3de56e65476ace0a8ecda4f4a4663684243d90e0512b + wildcard: "npm:^2.0.1" + checksum: 10c0/bf1429567858b353641801b8a2696ca0aac270fc8c55d4de8a7b586fe07d27fdcfc83099a98ab47e6162383db8dd63bb8cc25b1beb2ec82150422eec843b0dc0 languageName: node linkType: hard @@ -18006,7 +18130,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.9": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: @@ -18059,7 +18183,7 @@ __metadata: languageName: node linkType: hard -"wildcard@npm:^2.0.0": +"wildcard@npm:^2.0.1": version: 2.0.1 resolution: "wildcard@npm:2.0.1" checksum: 10c0/08f70cd97dd9a20aea280847a1fe8148e17cae7d231640e41eb26d2388697cbe65b67fd9e68715251c39b080c5ae4f76d71a9a69fa101d897273efdfb1b58bf7 @@ -18367,8 +18491,8 @@ __metadata: linkType: hard "ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.17.0": - version: 8.17.1 - resolution: "ws@npm:8.17.1" + version: 8.18.0 + resolution: "ws@npm:8.18.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -18377,7 +18501,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10c0/f4a49064afae4500be772abdc2211c8518f39e1c959640457dcee15d4488628620625c783902a52af2dd02f68558da2868fd06e6fd0e67ebcd09e6881b1b5bfe + checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06 languageName: node linkType: hard