Merge remote-tracking branch 'parent/main' into upstream-20240624

This commit is contained in:
KMY 2024-06-24 08:38:12 +09:00
commit af2727387e
88 changed files with 703 additions and 597 deletions

View file

@ -20,10 +20,6 @@ module.exports = defineConfig({
es6: true,
},
globals: {
ATTACHMENT_HOST: false,
},
parser: '@typescript-eslint/parser',
plugins: [
@ -79,7 +75,7 @@ module.exports = defineConfig({
],
},
],
'no-empty': 'off',
'no-empty': ['error', { "allowEmptyCatch": true }],
'no-restricted-properties': [
'error',
{ property: 'substring', message: 'Use .slice instead of .substring.' },
@ -94,7 +90,6 @@ module.exports = defineConfig({
message: "Use '·' (middle dot) instead of '•' (bullet)",
},
],
'no-self-assign': 'off',
'no-unused-expressions': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
@ -119,12 +114,10 @@ module.exports = defineConfig({
'react/jsx-tag-spacing': 'error',
'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/jsx-wrap-multilines': 'error',
'react/no-deprecated': 'off',
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error',
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46
'jsx-a11y/accessible-emoji': 'warn',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/label-has-associated-control': 'off',
'jsx-a11y/media-has-caption': 'off',
@ -139,23 +132,6 @@ module.exports = defineConfig({
// ],
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
// recommended rule is:
// 'jsx-a11y/no-noninteractive-element-interactions': [
// 'error',
// {
// body: ['onError', 'onLoad'],
// iframe: ['onError', 'onLoad'],
// img: ['onError', 'onLoad'],
// },
// ],
'jsx-a11y/no-noninteractive-element-interactions': [
'warn',
{
handlers: [
'onClick',
],
},
],
// recommended rule is:
// 'jsx-a11y/no-noninteractive-tabindex': [
// 'error',
// {
@ -165,7 +141,6 @@ module.exports = defineConfig({
// },
// ],
'jsx-a11y/no-noninteractive-tabindex': 'off',
'jsx-a11y/no-onchange': 'off',
// recommended is full 'error'
'jsx-a11y/no-static-element-interactions': [
'warn',

View file

@ -14,6 +14,9 @@
// 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

View file

@ -287,9 +287,13 @@ jobs:
- uses: actions/download-artifact@v4
with:
path: './public'
path: './'
name: ${{ github.sha }}
- name: Expand archived asset artifacts
run: |
tar xvzf artifacts.tar.gz
- name: Set up Ruby environment
uses: ./.github/actions/setup-ruby
with:
@ -407,7 +411,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
path: './public'
path: './'
name: ${{ github.sha }}
- name: Set up Ruby environment

2
.nvmrc
View file

@ -1 +1 @@
20.14
20.15

View file

@ -1,21 +1,14 @@
---
AllCops:
CacheRootDirectory: tmp
DisplayCopNames: true
DisplayStyleGuide: true
Exclude:
- db/schema.rb
- bin/*
- node_modules/**/*
- Vagrantfile
- vendor/**/*
- config/initializers/json_ld*
- lib/mastodon/migration_helpers.rb
- lib/templates/**/*
ExtraDetails: true
NewCops: enable
TargetRubyVersion: 3.1 # Oldest supported ruby version
UseCache: true
inherit_from:
- .rubocop/layout.yml

View file

@ -5,10 +5,6 @@ Rails/FilePath:
Rails/HttpStatus:
EnforcedStyle: numeric
Rails/LexicallyScopedActionFilter:
Exclude:
- app/controllers/auth/* # Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
Rails/NegateInclude:
Enabled: false

View file

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.63.5.
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.64.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@ -35,14 +35,6 @@ Rails/OutputSafety:
Exclude:
- 'config/initializers/simple_form.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: ==, equal?, eql?
Style/ClassEqualityComparison:
Exclude:
- 'app/helpers/jsonld_helper.rb'
- 'app/serializers/activitypub/outbox_serializer.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedVars.
Style/FetchEnvVar:
@ -59,7 +51,6 @@ Style/FetchEnvVar:
- 'config/initializers/vapid.rb'
- 'lib/mastodon/redis_config.rb'
- 'lib/tasks/repo.rake'
- 'spec/system/profile_spec.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
@ -70,40 +61,10 @@ Style/FormatStringToken:
- 'config/initializers/devise.rb'
- 'lib/paperclip/color_extractor.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/GlobalStdStream:
Exclude:
- 'config/environments/development.rb'
- 'config/environments/production.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
Exclude:
- 'app/lib/activitypub/activity/block.rb'
- 'app/lib/request.rb'
- 'app/lib/request_pool.rb'
- 'app/lib/webfinger.rb'
- 'app/lib/webfinger_resource.rb'
- 'app/models/concerns/account/counters.rb'
- 'app/models/concerns/user/ldap_authenticable.rb'
- 'app/models/tag.rb'
- 'app/models/user.rb'
- 'app/services/fan_out_on_write_service.rb'
- 'app/services/post_status_service.rb'
- 'app/services/process_hashtags_service.rb'
- 'app/workers/move_worker.rb'
- 'app/workers/redownload_avatar_worker.rb'
- 'app/workers/redownload_header_worker.rb'
- 'app/workers/redownload_media_worker.rb'
- 'app/workers/remote_account_refresh_worker.rb'
- 'config/initializers/devise.rb'
- 'lib/devise/strategies/two_factor_ldap_authenticatable.rb'
- 'lib/devise/strategies/two_factor_pam_authenticatable.rb'
- 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'lib/mastodon/cli/media.rb'
- 'lib/tasks/repo.rake'
Enabled: false
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/HashTransformValues:
@ -125,16 +86,10 @@ Style/MutableConstant:
- 'app/services/delete_account_service.rb'
- 'lib/mastodon/migration_warning.rb'
# This cop supports safe autocorrection (--autocorrect).
Style/NilLambda:
Exclude:
- 'config/initializers/paperclip.rb'
# Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter:
Exclude:
- 'app/helpers/admin/account_moderation_notes_helper.rb'
- 'app/helpers/jsonld_helper.rb'
- 'app/lib/admin/system_check/message.rb'
- 'app/lib/request.rb'
@ -158,13 +113,6 @@ Style/RedundantConstantBase:
- 'config/environments/production.rb'
- 'config/initializers/sidekiq.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try!
Style/SafeNavigation:
Exclude:
- 'app/models/concerns/account/finder_concern.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: WordRegex.
# SupportedStyles: percent, brackets

View file

@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1.7
# syntax=docker/dockerfile:1.8
# This file is designed for production server deployment, not local development work
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
@ -19,15 +19,15 @@ ARG NODE_MAJOR_VERSION="20"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm"
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm)
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.2.0-nightly.2023.11.09+something
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
# Example: v4.3.0-nightly.2023.11.09+pr-123456
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-12345"]
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
ARG MASTODON_VERSION_METADATA=""
# Allow Ruby on Rails to serve static files
@ -100,9 +100,7 @@ RUN \
apt-get dist-upgrade -yq; \
# Install jemalloc, curl and other necessary components
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
ffmpeg \
file \
libjemalloc2 \
patchelf \
@ -119,7 +117,7 @@ RUN \
;
# Create temporary build layer from base image
FROM ruby as build
FROM ruby AS build
# Copy Node package configuration files into working directory
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
@ -137,7 +135,10 @@ RUN \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Install build tools and bundler dependencies from APT
apt-get install -y --no-install-recommends \
autoconf \
automake \
build-essential \
cmake \
git \
libgdbm-dev \
libglib2.0-dev \
@ -146,9 +147,12 @@ RUN \
libidn-dev \
libpq-dev \
libssl-dev \
libtool \
meson \
nasm \
pkg-config \
shared-mime-info \
xz-utils \
# libvips components
libcgif-dev \
libexif-dev \
@ -162,6 +166,16 @@ RUN \
libspng-dev \
libtiff-dev \
libwebp-dev \
# ffmpeg components
libdav1d-dev \
liblzma-dev \
libmp3lame-dev \
libopus-dev \
libsnappy-dev \
libvorbis-dev \
libvpx-dev \
libx264-dev \
libx265-dev \
;
RUN \
@ -171,7 +185,7 @@ RUN \
corepack prepare --activate;
# Create temporary libvips specific build layer from build layer
FROM build as libvips
FROM build AS libvips
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
@ -190,8 +204,50 @@ RUN \
ninja; \
ninja install;
# Create temporary ffmpeg specific build layer from build layer
FROM build AS ffmpeg
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
ARG FFMPEG_VERSION=7.0.1
# ffmpeg download URL, change with [--build-arg FFMPEG_URL="https://ffmpeg.org/releases"]
ARG FFMPEG_URL=https://ffmpeg.org/releases
WORKDIR /usr/local/ffmpeg/src
RUN \
curl -sSL -o ffmpeg-${FFMPEG_VERSION}.tar.xz ${FFMPEG_URL}/ffmpeg-${FFMPEG_VERSION}.tar.xz; \
tar xf ffmpeg-${FFMPEG_VERSION}.tar.xz; \
cd ffmpeg-${FFMPEG_VERSION}; \
./configure \
--prefix=/usr/local/ffmpeg \
--toolchain=hardened \
--disable-debug \
--disable-devices \
--disable-doc \
--disable-ffplay \
--disable-network \
--disable-static \
--enable-ffmpeg \
--enable-ffprobe \
--enable-gpl \
--enable-libdav1d \
--enable-libmp3lame \
--enable-libopus \
--enable-libsnappy \
--enable-libvorbis \
--enable-libvpx \
--enable-libwebp \
--enable-libx264 \
--enable-libx265 \
--enable-shared \
--enable-version3 \
; \
make -j$(nproc); \
make install;
# Create temporary bundler specific build layer from build layer
FROM build as bundler
FROM build AS bundler
ARG TARGETPLATFORM
@ -213,7 +269,7 @@ RUN \
bundle install -j"$(nproc)";
# Create temporary node specific build layer from build layer
FROM build as yarn
FROM build AS yarn
ARG TARGETPLATFORM
@ -230,7 +286,7 @@ RUN \
yarn workspaces focus --production @mastodon/mastodon;
# Create temporary assets build layer from build layer
FROM build as precompiler
FROM build AS precompiler
# Copy Mastodon sources into precompiler layer
COPY . /opt/mastodon/
@ -254,7 +310,7 @@ RUN \
rm -fr /opt/mastodon/tmp;
# Prep final Mastodon Ruby layer
FROM ruby as mastodon
FROM ruby AS mastodon
ARG TARGETPLATFORM
@ -289,6 +345,20 @@ RUN \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
# ffmpeg components
libdav1d6 \
libmp3lame0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopus0 \
libsnappy1v5 \
libtheora0 \
libvorbis0a \
libvorbisenc2 \
libvorbisfile3 \
libvpx7 \
libx264-164 \
libx265-199 \
;
# Copy Mastodon sources into final layer
@ -302,11 +372,16 @@ COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
# Copy libvips components to layer
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
# Copy ffpmeg components to layer
COPY --from=ffmpeg /usr/local/ffmpeg/bin /usr/local/bin
COPY --from=ffmpeg /usr/local/ffmpeg/lib /usr/local/lib
RUN \
ldconfig; \
# Smoketest media processors
vips -v;
vips -v; \
ffmpeg -version; \
ffprobe -version;
RUN \
# Precompile bootsnap code for faster Rails startup

View file

@ -9,9 +9,6 @@ gem 'rack', '~> 2.2.7'
gem 'rails', '~> 7.1.1'
gem 'thor', '~> 1.2'
# For why irb is in the Gemfile, see: https://ruby.social/@st0012/111444685161478182
gem 'irb', '~> 1.8'
gem 'dotenv'
gem 'haml-rails', '~>2.0'
gem 'pg', '~> 1.5'
@ -61,6 +58,7 @@ gem 'httplog', '~> 1.7.0'
gem 'i18n'
gem 'idn-ruby', require: 'idn'
gem 'inline_svg'
gem 'irb', '~> 1.8'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
@ -71,7 +69,7 @@ gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'
gem 'parslet'
gem 'premailer-rails'
gem 'public_suffix', '~> 5.0'
gem 'public_suffix', '~> 6.0'
gem 'pundit', '~> 2.3'
gem 'rack-attack', '~> 6.6'
gem 'rack-cors', '~> 2.0', require: 'rack/cors'
@ -107,7 +105,7 @@ gem 'private_address_check', '~> 0.5'
gem 'opentelemetry-api', '~> 1.2.5'
group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.27.0', require: false
gem 'opentelemetry-exporter-otlp', '~> 0.28.0', require: false
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false

View file

@ -89,8 +89,8 @@ GEM
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
aes_key_wrap (1.1.0)
android_key_attestation (0.3.0)
annotate (3.2.0)
@ -143,7 +143,7 @@ GEM
brpoplpush-redis_script (0.1.3)
concurrent-ruby (~> 1.0, >= 1.0.5)
redis (>= 1.0, < 6)
builder (3.2.4)
builder (3.3.0)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
@ -195,7 +195,7 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-two-factor (5.0.0)
devise-two-factor (5.1.0)
activesupport (~> 7.0)
devise (~> 4.0)
railties (~> 7.0)
@ -226,7 +226,7 @@ GEM
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.7)
erubi (1.12.0)
erubi (1.13.0)
et-orbi (1.2.11)
tzinfo
excon (0.110.0)
@ -398,6 +398,7 @@ GEM
llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
rake (~> 13.0)
logger (1.6.0)
lograge (0.14.0)
actionpack (>= 4)
activesupport (>= 4)
@ -489,8 +490,8 @@ GEM
opentelemetry-api (1.2.5)
opentelemetry-common (0.20.1)
opentelemetry-api (~> 1.0)
opentelemetry-exporter-otlp (0.27.0)
google-protobuf (~> 3.14)
opentelemetry-exporter-otlp (0.28.0)
google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
@ -528,32 +529,27 @@ GEM
opentelemetry-instrumentation-concurrent_ruby (0.21.3)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-excon (0.22.1)
opentelemetry-instrumentation-excon (0.22.3)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-faraday (0.24.2)
opentelemetry-instrumentation-faraday (0.24.4)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-http (0.23.3)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-http_client (0.22.4)
opentelemetry-instrumentation-http_client (0.22.6)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-net_http (0.22.4)
opentelemetry-instrumentation-net_http (0.22.6)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-pg (0.27.3)
opentelemetry-api (~> 1.0)
opentelemetry-helpers-sql-obfuscation
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (0.24.3)
opentelemetry-instrumentation-rack (0.24.5)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.30.2)
opentelemetry-api (~> 1.0)
@ -564,13 +560,11 @@ GEM
opentelemetry-instrumentation-active_record (~> 0.7.0)
opentelemetry-instrumentation-active_support (~> 0.5.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-redis (0.25.4)
opentelemetry-instrumentation-redis (0.25.6)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-sidekiq (0.25.3)
opentelemetry-instrumentation-sidekiq (0.25.5)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.20.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-registry (0.3.1)
opentelemetry-api (~> 1.1)
@ -609,7 +603,7 @@ GEM
railties (>= 7.0.0)
psych (5.1.2)
stringio
public_suffix (5.1.1)
public_suffix (6.0.0)
puma (6.4.2)
nio4r (~> 2.0)
pundit (2.3.2)
@ -682,7 +676,7 @@ GEM
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.7.0)
rdf (~> 3.3)
rdoc (6.6.3.1)
rdoc (6.7.0)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
@ -709,7 +703,7 @@ GEM
rqrcode_core (1.2.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
rspec-expectations (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-github (2.4.0)
@ -717,7 +711,7 @@ GEM
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.2)
rspec-rails (6.1.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@ -778,8 +772,9 @@ GEM
scenic (1.8.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
selenium-webdriver (4.21.1)
selenium-webdriver (4.22.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@ -898,7 +893,7 @@ GEM
xorcist (1.1.3)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.15)
zeitwerk (2.6.16)
PLATFORMS
ruby
@ -979,7 +974,7 @@ DEPENDENCIES
omniauth-saml (~> 2.0)
omniauth_openid_connect (~> 0.6.1)
opentelemetry-api (~> 1.2.5)
opentelemetry-exporter-otlp (~> 0.27.0)
opentelemetry-exporter-otlp (~> 0.28.0)
opentelemetry-instrumentation-active_job (~> 0.7.1)
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
@ -1001,7 +996,7 @@ DEPENDENCIES
premailer-rails
private_address_check (~> 0.5)
propshaft
public_suffix (~> 5.0)
public_suffix (~> 6.0)
puma (~> 6.3)
pundit (~> 2.3)
rack (~> 2.2.7)

View file

@ -15,7 +15,7 @@ class Api::V2Alpha::NotificationsController < Api::BaseController
@relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
end
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification, max_id: @group_metadata.dig(notification.group_key, :max_id)) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
end
def show

View file

@ -25,6 +25,14 @@ class Auth::RegistrationsController < Devise::RegistrationsController
super(&:build_invite_request)
end
def edit # rubocop:disable Lint/UselessMethodDefinition
super
end
def create # rubocop:disable Lint/UselessMethodDefinition
super
end
def update
super do |resource|
resource.clear_other_sessions(current_session.session_id) if resource.saved_change_to_encrypted_password?

View file

@ -149,7 +149,7 @@ module JsonLdHelper
def safe_for_forwarding?(original, compacted)
original.without('@context', 'signature').all? do |key, value|
compacted_value = compacted[key]
return false unless value.class == compacted_value.class
return false unless value.instance_of?(compacted_value.class)
if value.is_a?(Hash)
safe_for_forwarding?(value, compacted_value)

View file

@ -0,0 +1,16 @@
import {
apiGetNotificationPolicy,
apiUpdateNotificationsPolicy,
} from 'mastodon/api/notification_policies';
import type { NotificationPolicy } from 'mastodon/models/notification_policy';
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
export const fetchNotificationPolicy = createDataLoadingThunk(
'notificationPolicy/fetch',
() => apiGetNotificationPolicy(),
);
export const updateNotificationsPolicy = createDataLoadingThunk(
'notificationPolicy/update',
(policy: Partial<NotificationPolicy>) => apiUpdateNotificationsPolicy(policy),
);

View file

@ -45,10 +45,6 @@ export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ';
export const NOTIFICATIONS_SET_BROWSER_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
export const NOTIFICATION_POLICY_FETCH_REQUEST = 'NOTIFICATION_POLICY_FETCH_REQUEST';
export const NOTIFICATION_POLICY_FETCH_SUCCESS = 'NOTIFICATION_POLICY_FETCH_SUCCESS';
export const NOTIFICATION_POLICY_FETCH_FAIL = 'NOTIFICATION_POLICY_FETCH_FAIL';
export const NOTIFICATION_REQUESTS_FETCH_REQUEST = 'NOTIFICATION_REQUESTS_FETCH_REQUEST';
export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS';
export const NOTIFICATION_REQUESTS_FETCH_FAIL = 'NOTIFICATION_REQUESTS_FETCH_FAIL';
@ -363,40 +359,6 @@ export function setBrowserPermission (value) {
};
}
export const fetchNotificationPolicy = () => (dispatch) => {
dispatch(fetchNotificationPolicyRequest());
api().get('/api/v1/notifications/policy').then(({ data }) => {
dispatch(fetchNotificationPolicySuccess(data));
}).catch(err => {
dispatch(fetchNotificationPolicyFail(err));
});
};
export const fetchNotificationPolicyRequest = () => ({
type: NOTIFICATION_POLICY_FETCH_REQUEST,
});
export const fetchNotificationPolicySuccess = policy => ({
type: NOTIFICATION_POLICY_FETCH_SUCCESS,
policy,
});
export const fetchNotificationPolicyFail = error => ({
type: NOTIFICATION_POLICY_FETCH_FAIL,
error,
});
export const updateNotificationsPolicy = params => (dispatch) => {
dispatch(fetchNotificationPolicyRequest());
api().put('/api/v1/notifications/policy', params).then(({ data }) => {
dispatch(fetchNotificationPolicySuccess(data));
}).catch(err => {
dispatch(fetchNotificationPolicyFail(err));
});
};
export const fetchNotificationRequests = () => (dispatch, getState) => {
const params = {};

View file

@ -78,7 +78,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
},
onDisconnect() {
dispatch(disconnectTimeline(timelineId));
dispatch(disconnectTimeline({ timeline: timelineId }));
if (options.fallback) {
// @ts-expect-error

View file

@ -6,9 +6,11 @@ import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
import { importFetchedStatus, importFetchedStatuses } from './importer';
import { submitMarkers } from './markers';
import {timelineDelete} from './timelines_typed';
export { disconnectTimeline } from './timelines_typed';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
@ -17,7 +19,6 @@ export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const TIMELINE_MARK_AS_PARTIAL = 'TIMELINE_MARK_AS_PARTIAL';
@ -62,16 +63,10 @@ export function updateTimeline(timeline, status, accept) {
export function deleteFromTimelines(id) {
return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']);
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id'));
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id')).valueSeq().toJSON();
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
dispatch({
type: TIMELINE_DELETE,
id,
accountId,
references,
reblogOf,
});
dispatch(timelineDelete({ statusId: id, accountId, references, reblogOf }));
};
}
@ -227,12 +222,6 @@ export function connectTimeline(timeline) {
};
}
export const disconnectTimeline = timeline => ({
type: TIMELINE_DISCONNECT,
timeline,
usePendingItems: preferPendingItems,
});
export const markAsPartial = timeline => ({
type: TIMELINE_MARK_AS_PARTIAL,
timeline,

View file

@ -0,0 +1,20 @@
import { createAction } from '@reduxjs/toolkit';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
export const disconnectTimeline = createAction(
'timeline/disconnect',
({ timeline }: { timeline: string }) => ({
payload: {
timeline,
usePendingItems: preferPendingItems,
},
}),
);
export const timelineDelete = createAction<{
statusId: string;
accountId: string;
references: string[];
reblogOf: string | null;
}>('timelines/delete');

View file

@ -0,0 +1,10 @@
import { apiRequest } from 'mastodon/api';
import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies';
export const apiGetNotificationPolicy = () =>
apiRequest<NotificationPolicyJSON>('GET', '/v1/notifications/policy');
export const apiUpdateNotificationsPolicy = (
policy: Partial<NotificationPolicyJSON>,
) =>
apiRequest<NotificationPolicyJSON>('PUT', '/v1/notifications/policy', policy);

View file

@ -0,0 +1,12 @@
// See app/serializers/rest/notification_policy_serializer.rb
export interface NotificationPolicyJSON {
filter_not_following: boolean;
filter_not_followers: boolean;
filter_new_accounts: boolean;
filter_private_mentions: boolean;
summary: {
pending_requests_count: number;
pending_notifications_count: number;
};
}

View file

@ -133,7 +133,7 @@ class AccountTimeline extends ImmutablePureComponent {
}
if (prevProps.accountId === me && accountId !== me) {
dispatch(disconnectTimeline(`account:${me}`));
dispatch(disconnectTimeline({ timeline: `account:${me}` }));
}
}
@ -141,7 +141,7 @@ class AccountTimeline extends ImmutablePureComponent {
const { dispatch, accountId } = this.props;
if (accountId === me) {
dispatch(disconnectTimeline(`account:${me}`));
dispatch(disconnectTimeline({ timeline: `account:${me}` }));
}
}

View file

@ -25,7 +25,7 @@ class ColumnSettings extends PureComponent {
alertsEnabled: PropTypes.bool,
browserSupport: PropTypes.bool,
browserPermission: PropTypes.string,
notificationPolicy: ImmutablePropTypes.map,
notificationPolicy: PropTypes.object.isRequired,
onChangePolicy: PropTypes.func.isRequired,
};
@ -88,22 +88,22 @@ class ColumnSettings extends PureComponent {
<h3><FormattedMessage id='notifications.policy.title' defaultMessage='Filter out notifications from…' /></h3>
<div className='column-settings__row'>
<CheckboxWithLabel checked={notificationPolicy.get('filter_not_following')} onChange={this.handleFilterNotFollowing}>
<CheckboxWithLabel checked={notificationPolicy.filter_not_following} onChange={this.handleFilterNotFollowing}>
<strong><FormattedMessage id='notifications.policy.filter_not_following_title' defaultMessage="People you don't follow" /></strong>
<span className='hint'><FormattedMessage id='notifications.policy.filter_not_following_hint' defaultMessage='Until you manually approve them' /></span>
</CheckboxWithLabel>
<CheckboxWithLabel checked={notificationPolicy.get('filter_not_followers')} onChange={this.handleFilterNotFollowers}>
<CheckboxWithLabel checked={notificationPolicy.filter_not_followers} onChange={this.handleFilterNotFollowers}>
<strong><FormattedMessage id='notifications.policy.filter_not_followers_title' defaultMessage='People not following you' /></strong>
<span className='hint'><FormattedMessage id='notifications.policy.filter_not_followers_hint' defaultMessage='Including people who have been following you fewer than {days, plural, one {one day} other {# days}}' values={{ days: 3 }} /></span>
</CheckboxWithLabel>
<CheckboxWithLabel checked={notificationPolicy.get('filter_new_accounts')} onChange={this.handleFilterNewAccounts}>
<CheckboxWithLabel checked={notificationPolicy.filter_new_accounts} onChange={this.handleFilterNewAccounts}>
<strong><FormattedMessage id='notifications.policy.filter_new_accounts_title' defaultMessage='New accounts' /></strong>
<span className='hint'><FormattedMessage id='notifications.policy.filter_new_accounts.hint' defaultMessage='Created within the past {days, plural, one {one day} other {# days}}' values={{ days: 30 }} /></span>
</CheckboxWithLabel>
<CheckboxWithLabel checked={notificationPolicy.get('filter_private_mentions')} onChange={this.handleFilterPrivateMentions}>
<CheckboxWithLabel checked={notificationPolicy.filter_private_mentions} onChange={this.handleFilterPrivateMentions}>
<strong><FormattedMessage id='notifications.policy.filter_private_mentions_title' defaultMessage='Unsolicited private mentions' /></strong>
<span className='hint'><FormattedMessage id='notifications.policy.filter_private_mentions_hint' defaultMessage="Filtered unless it's in reply to your own mention or if you follow the sender" /></span>
</CheckboxWithLabel>

View file

@ -1,49 +0,0 @@
import { useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
import { fetchNotificationPolicy } from 'mastodon/actions/notifications';
import { Icon } from 'mastodon/components/icon';
import { toCappedNumber } from 'mastodon/utils/numbers';
export const FilteredNotificationsBanner = () => {
const dispatch = useDispatch();
const policy = useSelector(state => state.get('notificationPolicy'));
useEffect(() => {
dispatch(fetchNotificationPolicy());
const interval = setInterval(() => {
dispatch(fetchNotificationPolicy());
}, 120000);
return () => {
clearInterval(interval);
};
}, [dispatch]);
if (policy === null || policy.getIn(['summary', 'pending_notifications_count']) === 0) {
return null;
}
return (
<Link className='filtered-notifications-banner' to='/notifications/requests'>
<Icon icon={InventoryIcon} />
<div className='filtered-notifications-banner__text'>
<strong><FormattedMessage id='filtered_notifications_banner.title' defaultMessage='Filtered notifications' /></strong>
<span><FormattedMessage id='filtered_notifications_banner.pending_requests' defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' values={{ count: policy.getIn(['summary', 'pending_requests_count']) }} /></span>
</div>
<div className='filtered-notifications-banner__badge'>
<div className='filtered-notifications-banner__badge__badge'>{toCappedNumber(policy.getIn(['summary', 'pending_notifications_count']))}</div>
<FormattedMessage id='filtered_notifications_banner.mentions' defaultMessage='{count, plural, one {mention} other {mentions}}' values={{ count: policy.getIn(['summary', 'pending_notifications_count']) }} />
</div>
</Link>
);
};

View file

@ -0,0 +1,68 @@
import { useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
import { fetchNotificationPolicy } from 'mastodon/actions/notification_policies';
import { Icon } from 'mastodon/components/icon';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { toCappedNumber } from 'mastodon/utils/numbers';
export const FilteredNotificationsBanner: React.FC = () => {
const dispatch = useAppDispatch();
const policy = useAppSelector((state) => state.notificationPolicy);
useEffect(() => {
void dispatch(fetchNotificationPolicy());
const interval = setInterval(() => {
void dispatch(fetchNotificationPolicy());
}, 120000);
return () => {
clearInterval(interval);
};
}, [dispatch]);
if (policy === null || policy.summary.pending_notifications_count === 0) {
return null;
}
return (
<Link
className='filtered-notifications-banner'
to='/notifications/requests'
>
<Icon icon={InventoryIcon} id='filtered-notifications' />
<div className='filtered-notifications-banner__text'>
<strong>
<FormattedMessage
id='filtered_notifications_banner.title'
defaultMessage='Filtered notifications'
/>
</strong>
<span>
<FormattedMessage
id='filtered_notifications_banner.pending_requests'
defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know'
values={{ count: policy.summary.pending_requests_count }}
/>
</span>
</div>
<div className='filtered-notifications-banner__badge'>
<div className='filtered-notifications-banner__badge__badge'>
{toCappedNumber(policy.summary.pending_notifications_count)}
</div>
<FormattedMessage
id='filtered_notifications_banner.mentions'
defaultMessage='{count, plural, one {mention} other {mentions}}'
values={{ count: policy.summary.pending_notifications_count }}
/>
</div>
</Link>
);
};

View file

@ -4,7 +4,8 @@ import { connect } from 'react-redux';
import { showAlert } from '../../../actions/alerts';
import { openModal } from '../../../actions/modal';
import { setFilter, clearNotifications, requestBrowserPermission, updateNotificationsPolicy } from '../../../actions/notifications';
import { updateNotificationsPolicy } from '../../../actions/notification_policies';
import { setFilter, clearNotifications, requestBrowserPermission } from '../../../actions/notifications';
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
import { changeSetting } from '../../../actions/settings';
import ColumnSettings from '../components/column_settings';
@ -15,13 +16,16 @@ const messages = defineMessages({
permissionDenied: { id: 'notifications.permission_denied_alert', defaultMessage: 'Desktop notifications can\'t be enabled, as browser permission has been denied before' },
});
/**
* @param {import('mastodon/store').RootState} state
*/
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'notifications']),
pushSettings: state.get('push_notifications'),
alertsEnabled: state.getIn(['settings', 'notifications', 'alerts']).includes(true),
browserSupport: state.getIn(['notifications', 'browserSupport']),
browserPermission: state.getIn(['notifications', 'browserPermission']),
notificationPolicy: state.get('notificationPolicy'),
notificationPolicy: state.notificationPolicy,
});
const mapDispatchToProps = (dispatch, { intl }) => ({

View file

@ -147,14 +147,14 @@
"compose.published.body": "Postitus avaldatud.",
"compose.published.open": "Ava",
"compose.saved.body": "Postitus salvestatud.",
"compose_form.direct_message_warning_learn_more": "Vaata täpsemalt",
"compose_form.direct_message_warning_learn_more": "Vaata lisa",
"compose_form.encryption_warning": "Postitused Mastodonis ei ole otsast-otsani krüpteeritud. Ära jaga mingeid delikaatseid andmeid Mastodoni kaudu.",
"compose_form.hashtag_warning": "See postitus ei ilmu ühegi märksõna all, kuna pole avalik. Vaid avalikud postitused on märksõnade kaudu leitavad.",
"compose_form.lock_disclaimer": "Su konto ei ole {locked}. Igaüks saab sind jälgida, et näha su ainult-jälgijatele postitusi.",
"compose_form.lock_disclaimer.lock": "lukus",
"compose_form.placeholder": "Millest mõtled?",
"compose_form.poll.duration": "Küsitluse kestus",
"compose_form.poll.multiple": "Valikvastustega",
"compose_form.poll.multiple": "Mitu vastust",
"compose_form.poll.option_placeholder": "Valik {number}",
"compose_form.poll.single": "Vali üks",
"compose_form.poll.switch_to_multiple": "Muuda küsitlust mitmikvaliku lubamiseks",
@ -297,6 +297,7 @@
"filter_modal.select_filter.subtitle": "Kasuta olemasolevat kategooriat või loo uus",
"filter_modal.select_filter.title": "Filtreeri seda postitust",
"filter_modal.title.status": "Postituse filtreerimine",
"filtered_notifications_banner.mentions": "{count, plural, one {mainimine} other {mainimist}}",
"filtered_notifications_banner.pending_requests": "Teateid {count, plural, =0 {mitte üheltki} one {ühelt} other {#}} inimeselt, keda võid teada",
"filtered_notifications_banner.title": "Filtreeritud teavitused",
"firehose.all": "Kõik",
@ -305,15 +306,19 @@
"follow_request.authorize": "Autoriseeri",
"follow_request.reject": "Hülga",
"follow_requests.unlocked_explanation": "Kuigi su konto pole lukustatud, soovitab {domain} personal siiski nende kontode jälgimistaotlused käsitsi üle vaadata.",
"follow_suggestions.curated_suggestion": "Teiste valitud",
"follow_suggestions.curated_suggestion": "Meeskonna valitud",
"follow_suggestions.dismiss": "Ära enam näita",
"follow_suggestions.hints.featured": "Selle kasutajaprofiili on soovitanud {domain} kasutajad.",
"follow_suggestions.hints.friends_of_friends": "See kasutajaprofiil on jälgitavate seas populaarne.",
"follow_suggestions.featured_longer": "Käsitsi valitud {domain} meeskonna poolt",
"follow_suggestions.friends_of_friends_longer": "Populaarne inimeste hulgas, keda jälgid",
"follow_suggestions.hints.featured": "Selle kasutajaprofiili on soovitanud {domain} meeskond.",
"follow_suggestions.hints.friends_of_friends": "See kasutajaprofiil on sinu jälgitavate seas populaarne.",
"follow_suggestions.hints.most_followed": "See on {domain} enim jälgitud kasutajaprofiil.",
"follow_suggestions.hints.most_interactions": "See on {domain} viimasel ajal enim tähelepanu saanud kasutajaprofiil.",
"follow_suggestions.hints.most_interactions": "See kasutajaprofiil on viimasel ajal {domain} saanud palju tähelepanu.",
"follow_suggestions.hints.similar_to_recently_followed": "See kasutajaprofiil sarnaneb neile, mida oled hiljuti jälgima asunud.",
"follow_suggestions.personalized_suggestion": "Isikupärastatud soovitus",
"follow_suggestions.popular_suggestion": "Popuplaarne soovitus",
"follow_suggestions.popular_suggestion_longer": "Populaarne kohas {domain}",
"follow_suggestions.similar_to_recently_followed_longer": "Sarnane profiilile, mida hiljuti jälgima hakkasid",
"follow_suggestions.view_all": "Vaata kõiki",
"follow_suggestions.who_to_follow": "Keda jälgida",
"followed_tags": "Jälgitavad märksõnad",
@ -409,6 +414,8 @@
"limited_account_hint.action": "Näita profilli sellegipoolest",
"limited_account_hint.title": "See profiil on peidetud {domain} moderaatorite poolt.",
"link_preview.author": "{name} poolt",
"link_preview.more_from_author": "Veel kasutajalt {name}",
"link_preview.shares": "{count, plural, one {{counter} postitus} other {{counter} postitust}}",
"lists.account.add": "Lisa nimekirja",
"lists.account.remove": "Eemalda nimekirjast",
"lists.delete": "Kustuta nimekiri",
@ -468,13 +475,22 @@
"notification.follow": "{name} alustas su jälgimist",
"notification.follow_request": "{name} soovib sind jälgida",
"notification.mention": "{name} mainis sind",
"notification.moderation-warning.learn_more": "Vaata lisa",
"notification.moderation_warning": "Said modereerimise hoiatuse",
"notification.moderation_warning.action_delete_statuses": "Mõni su postitus on eemaldatud.",
"notification.moderation_warning.action_disable": "Su konto on keelatud.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Mõni su postitustest on märgitud kui tundlik.",
"notification.moderation_warning.action_none": "Su konto on saanud modereerimise hoiatuse.",
"notification.moderation_warning.action_sensitive": "Su postitused märgitakse nüüdsest tundlikuks.",
"notification.moderation_warning.action_silence": "Su kontole pandi piirang.",
"notification.moderation_warning.action_suspend": "Su konto on peatatud.",
"notification.own_poll": "Su küsitlus on lõppenud",
"notification.poll": "Küsitlus, milles osalesid, on lõppenud",
"notification.reblog": "{name} jagas edasi postitust",
"notification.relationships_severance_event": "Kadunud ühendus kasutajaga {name}",
"notification.relationships_severance_event.account_suspension": "{from} admin on kustutanud {target}, mis tähendab, et sa ei saa enam neilt uuendusi või suhelda nendega.",
"notification.relationships_severance_event.domain_block": "{from} admin on blokeerinud {target}, sealhulgas {followersCount} sinu jälgijat ja {followingCount, plural, one {# konto} other {# kontot}}, mida jälgid.",
"notification.relationships_severance_event.learn_more": "Saa rohkem teada",
"notification.relationships_severance_event.learn_more": "Vaata lisa",
"notification.relationships_severance_event.user_domain_block": "Blokeerisid {target}, eemaldades oma jälgijate hulgast {followersCount} ja jälgitavate hulgast {followingCount, plural, one {# konto} other {# kontot}}.",
"notification.status": "{name} just postitas",
"notification.update": "{name} muutis postitust",
@ -680,8 +696,11 @@
"server_banner.about_active_users": "Inimesed, kes kasutavad seda serverit viimase 30 päeva jooksul (kuu aktiivsed kasutajad)",
"server_banner.active_users": "aktiivsed kasutajad",
"server_banner.administered_by": "Administraator:",
"server_banner.is_one_of_many": "{domain} on üks paljudest sõltumatutest Mastodoni serveritest, mida saab fediversumis osalemiseks kasutada.",
"server_banner.server_stats": "Serveri statistika:",
"sign_in_banner.create_account": "Loo konto",
"sign_in_banner.follow_anyone": "Jälgi ükskõik keda kogu fediversumist ja näe kõike ajalises järjestuses. Ei mingeid algoritme, reklaame või klikipüüdjaid segamas.",
"sign_in_banner.mastodon_is": "Mastodon on parim viis olemaks kursis sellega, mis toimub.",
"sign_in_banner.sign_in": "Logi sisse",
"sign_in_banner.sso_redirect": "Sisene või registreeru",
"status.admin_account": "Ava @{name} moderaatorivaates",

View file

@ -80,6 +80,7 @@
"admin.dashboard.retention.cohort_size": "नये उपयोगकर्ता",
"admin.impact_report.instance_accounts": "ये अकाउंट प्रोफाइल मिटा देगा",
"admin.impact_report.instance_followers": "हमारे यूजर्स इन फॉलोअर्स को खो देंगे",
"admin.impact_report.instance_follows": "उनके उपयोगकर्ता इतने फ़ॉलोअर खो देंगे",
"admin.impact_report.title": "प्रभावकां सारांश",
"alert.rate_limited.message": "कृप्या {retry_time, time, medium} के बाद दुबारा कोशिश करें",
"alert.rate_limited.title": "सीमित दर",
@ -88,6 +89,7 @@
"announcement.announcement": "घोषणा",
"attachments_list.unprocessed": "(असंसाधित)",
"audio.hide": "हाईड ऑडियो",
"block_modal.remote_users_caveat": "हम {domain} को आपके निर्णय का सम्मान करने को कहेंगे।हालाकि इसकी आपूर्ति कि प्रत्याभूति नहीं हे। क्योंकि कुछ सर्वर ब्लॉक को अलग तरह से निभा सकते हे। अभी भी सार्वजानिक पोस्ट लोग- इन बगैर के उपयोगकर्ताओं को दिख सकती हैं।",
"block_modal.show_less": "कम दिखाएं",
"block_modal.show_more": "और दिखाएँ",
"block_modal.they_cant_mention": "वे आपको मेंशन या फॉलो नहीं कर सकते",
@ -205,7 +207,12 @@
"dismissable_banner.dismiss": "डिसमिस",
"dismissable_banner.explore_links": "इन समाचारों के बारे में लोगों द्वारा इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर अभी बात की जा रही है।",
"dismissable_banner.explore_tags": "ये हैशटैग अभी इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर लोगों के बीच कर्षण प्राप्त कर रहे हैं।",
"dismissable_banner.public_timeline": "यह ताजा सार्वजनिक पोस्ट है जिसका सामाजिक वेब {domain} के लोगो द्वारा अनुसरण हो रहा हैं।",
"domain_block_modal.block": "सर्वर ब्लॉक करें",
"domain_block_modal.block_account_instead": "इसकी जगह यह @{name} रखें",
"domain_block_modal.they_can_interact_with_old_posts": "इस सर्वर की लोग आपकी पूरानी पोस्ट्स का अनुसरण किया जा sakta है।",
"domain_block_modal.they_cant_follow": "इस सर्वर मेसे कोई भी आपका अनुसरण नहीं कर सकता।",
"domain_block_modal.they_wont_know": "उनको पता नहीं चलेगा कि वे अवरोधित किए गए है।",
"domain_block_modal.title": "डोमेन ब्लॉक करें",
"domain_pill.server": "सर्वर",
"domain_pill.username": "यूज़रनेम",

View file

@ -696,8 +696,10 @@
"server_banner.about_active_users": "Personas que ha usate iste servitor in le ultime 30 dies (usatores active per mense)",
"server_banner.active_users": "usatores active",
"server_banner.administered_by": "Administrate per:",
"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.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",
"status.admin_account": "Aperir le interfacie de moderation pro @{name}",

View file

@ -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": "{counter} 팔로워",
@ -53,7 +53,7 @@
"account.mute_notifications_short": "알림 뮤트",
"account.mute_short": "뮤트",
"account.muted": "뮤트됨",
"account.mutual": "상호 팔로우",
"account.mutual": "맞팔로우 중",
"account.no_bio": "제공된 설명이 없습니다.",
"account.open_original_page": "원본 페이지 열기",
"account.posts": "게시물",

View file

@ -0,0 +1,3 @@
import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies';
export type NotificationPolicy = NotificationPolicyJSON; // No changes from the API type

View file

@ -1,5 +1,7 @@
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import {
COMPOSE_MOUNT,
COMPOSE_UNMOUNT,
@ -57,7 +59,6 @@ import {
} from '../actions/compose';
import { REDRAFT } from '../actions/statuses';
import { STORE_HYDRATE } from '../actions/store';
import { TIMELINE_DELETE } from '../actions/timelines';
import { enabledVisibilites, me } from '../initial_state';
import { unescapeHTML } from '../utils/html';
import { uuid } from '../uuid';
@ -565,14 +566,14 @@ export default function compose(state = initialState, action) {
return updateSuggestionTags(state, action.token);
case COMPOSE_TAG_HISTORY_UPDATE:
return state.set('tagHistory', fromJS(action.tags));
case TIMELINE_DELETE:
if (action.id === state.get('in_reply_to')) {
case timelineDelete.type:
if (action.payload.statusId === state.get('in_reply_to')) {
if (state.get('privacy') === 'reply') {
return state.set('in_reply_to', null).set('privacy', 'circle');
} else {
return state.set('in_reply_to', null);
}
} else if (action.id === state.get('id')) {
} else if (action.payload.statusId === state.get('id')) {
return state.set('id', null);
} else {
return state;

View file

@ -1,11 +1,13 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import {
blockAccountSuccess,
muteAccountSuccess,
} from '../actions/accounts';
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
import { TIMELINE_DELETE, TIMELINE_UPDATE } from '../actions/timelines';
import { TIMELINE_UPDATE } from '../actions/timelines';
import { compareId } from '../compare_id';
const initialState = ImmutableMap({
@ -102,8 +104,8 @@ export default function replies(state = initialState, action) {
return filterContexts(state, action.payload.relationship, action.payload.statuses);
case CONTEXT_FETCH_SUCCESS:
return normalizeContext(state, action.id, action.ancestors, action.descendants, action.references);
case TIMELINE_DELETE:
return deleteFromContexts(state, [action.id]);
case timelineDelete.type:
return deleteFromContexts(state, [action.payload.statusId]);
case TIMELINE_UPDATE:
return updateContext(state, action.status);
default:

View file

@ -1,10 +1,11 @@
import type { Reducer } from '@reduxjs/toolkit';
import { Record as ImmutableRecord, Stack } from 'immutable';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import { COMPOSE_UPLOAD_CHANGE_SUCCESS } from '../actions/compose';
import type { ModalType } from '../actions/modal';
import { openModal, closeModal } from '../actions/modal';
import { TIMELINE_DELETE } from '../actions/timelines';
export type ModalProps = Record<string, unknown>;
interface Modal {
@ -72,10 +73,10 @@ export const modalReducer: Reducer<State> = (state = initialState, action) => {
// TODO: type those actions
else if (action.type === COMPOSE_UPLOAD_CHANGE_SUCCESS)
return popModal(state, { modalType: 'FOCAL_POINT', ignoreFocus: false });
else if (action.type === TIMELINE_DELETE)
else if (timelineDelete.match(action))
return state.update('stack', (stack) =>
stack.filterNot(
(modal) => modal.get('modalProps').statusId === action.id,
(modal) => modal.get('modalProps').statusId === action.payload.statusId,
),
);
else return state;

View file

@ -1,12 +0,0 @@
import { fromJS } from 'immutable';
import { NOTIFICATION_POLICY_FETCH_SUCCESS } from 'mastodon/actions/notifications';
export const notificationPolicyReducer = (state = null, action) => {
switch(action.type) {
case NOTIFICATION_POLICY_FETCH_SUCCESS:
return fromJS(action.policy);
default:
return state;
}
};

View file

@ -0,0 +1,18 @@
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
import {
fetchNotificationPolicy,
updateNotificationsPolicy,
} from 'mastodon/actions/notification_policies';
import type { NotificationPolicy } from 'mastodon/models/notification_policy';
export const notificationPolicyReducer =
createReducer<NotificationPolicy | null>(null, (builder) => {
builder.addMatcher(
isAnyOf(
fetchNotificationPolicy.fulfilled,
updateNotificationsPolicy.fulfilled,
),
(_state, action) => action.payload,
);
});

View file

@ -1,6 +1,7 @@
import { fromJS, Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { blockDomainSuccess } from 'mastodon/actions/domain_blocks';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import {
authorizeFollowRequestSuccess,
@ -30,7 +31,7 @@ import {
NOTIFICATIONS_SET_BROWSER_SUPPORT,
NOTIFICATIONS_SET_BROWSER_PERMISSION,
} from '../actions/notifications';
import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
import { disconnectTimeline } from '../actions/timelines';
import { compareId } from '../compare_id';
const initialState = ImmutableMap({
@ -293,11 +294,11 @@ export default function notifications(state = initialState, action) {
return filterNotifications(state, [action.payload.id], 'follow_request');
case NOTIFICATIONS_CLEAR:
return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
case TIMELINE_DELETE:
return deleteByStatus(state, action.id);
case TIMELINE_DISCONNECT:
return action.timeline === 'home' ?
state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
case timelineDelete.type:
return deleteByStatus(state, action.payload.statusId);
case disconnectTimeline.type:
return action.payload.timeline === 'home' ?
state.update(action.payload.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
state;
case NOTIFICATIONS_MARK_AS_READ:
const lastNotification = state.get('items').find(item => item !== null);

View file

@ -4,8 +4,7 @@ import {
deployPictureInPictureAction,
removePictureInPicture,
} from 'mastodon/actions/picture_in_picture';
import { TIMELINE_DELETE } from '../actions/timelines';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
export interface PIPMediaProps {
src: string;
@ -49,8 +48,9 @@ export const pictureInPictureReducer: Reducer<PIPState> = (
...action.payload.props,
};
else if (removePictureInPicture.match(action)) return initialState;
else if (action.type === TIMELINE_DELETE)
if (state.type && state.statusId === action.id) return initialState;
else if (timelineDelete.match(action))
if (state.type && state.statusId === action.payload.statusId)
return initialState;
return state;
};

View file

@ -1,5 +1,6 @@
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import { me } from 'mastodon/initial_state';
import {
@ -34,7 +35,6 @@ import {
STATUS_FETCH_FAIL,
STATUS_EMOJI_REACTION_UPDATE,
} from '../actions/statuses';
import { TIMELINE_DELETE } from '../actions/timelines';
const importStatus = (state, status) => state.set(status.id, fromJS(status));
@ -152,8 +152,8 @@ export default function statuses(state = initialState, action) {
});
case STATUS_COLLAPSE:
return state.setIn([action.id, 'collapsed'], action.isCollapsed);
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.references);
case timelineDelete.type:
return deleteStatus(state, action.payload.statusId, action.payload.references);
case STATUS_TRANSLATE_SUCCESS:
return statusTranslateSuccess(state, action.id, action.translation);
case STATUS_TRANSLATE_UNDO:

View file

@ -1,5 +1,7 @@
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
import { timelineDelete } from 'mastodon/actions/timelines_typed';
import {
blockAccountSuccess,
muteAccountSuccess,
@ -7,19 +9,18 @@ import {
} from '../actions/accounts';
import {
TIMELINE_UPDATE,
TIMELINE_DELETE,
TIMELINE_CLEAR,
TIMELINE_EXPAND_SUCCESS,
TIMELINE_EXPAND_REQUEST,
TIMELINE_EXPAND_FAIL,
TIMELINE_SCROLL_TOP,
TIMELINE_CONNECT,
TIMELINE_DISCONNECT,
TIMELINE_LOAD_PENDING,
TIMELINE_MARK_AS_PARTIAL,
TIMELINE_INSERT,
TIMELINE_GAP,
TIMELINE_SUGGESTIONS,
disconnectTimeline,
} from '../actions/timelines';
import { compareId } from '../compare_id';
@ -158,7 +159,7 @@ const filterTimelines = (state, relationship, statuses) => {
return;
}
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id')).valueSeq().toJSON();
state = deleteStatus(state, status.get('id'), references, relationship.id);
});
@ -201,8 +202,8 @@ export default function timelines(state = initialState, action) {
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent, action.usePendingItems);
case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems);
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.references, action.reblogOf);
case timelineDelete.type:
return deleteStatus(state, action.payload.statusId, action.payload.references, action.payload.reblogOf);
case TIMELINE_CLEAR:
return clearTimeline(state, action.timeline);
case blockAccountSuccess.type:
@ -214,11 +215,11 @@ export default function timelines(state = initialState, action) {
return updateTop(state, action.timeline, action.top);
case TIMELINE_CONNECT:
return state.update(action.timeline, initialTimeline, map => reconnectTimeline(map, action.usePendingItems));
case TIMELINE_DISCONNECT:
case disconnectTimeline.type:
return state.update(
action.timeline,
action.payload.timeline,
initialTimeline,
map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(TIMELINE_GAP) : items),
map => map.set('online', false).update(action.payload.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(TIMELINE_GAP) : items),
);
case TIMELINE_MARK_AS_PARTIAL:
return state.update(

View file

@ -82,13 +82,19 @@ export function createThunk<Arg = void, Returned = void>(
const discardLoadDataInPayload = Symbol('discardLoadDataInPayload');
type DiscardLoadData = typeof discardLoadDataInPayload;
type OnData<LoadDataResult, ReturnedData> = (
type OnData<ActionArg, LoadDataResult, ReturnedData> = (
data: LoadDataResult,
api: AppThunkApi & {
actionArg: ActionArg;
discardLoadData: DiscardLoadData;
},
) => ReturnedData | DiscardLoadData | Promise<ReturnedData | DiscardLoadData>;
type LoadData<Args, LoadDataResult> = (
args: Args,
api: AppThunkApi,
) => Promise<LoadDataResult>;
type ArgsType = Record<string, unknown> | undefined;
// Overload when there is no `onData` method, the payload is the `onData` result
@ -101,18 +107,18 @@ export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
loadData: LoadData<Args, LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<LoadDataResult, DiscardLoadData>,
| OnData<Args, LoadDataResult, DiscardLoadData>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, void>>;
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
loadData: LoadData<Args, LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<Args, LoadDataResult, void>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
@ -123,8 +129,10 @@ export function createDataLoadingThunk<
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
loadData: LoadData<Args, LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<Args, LoadDataResult, Returned>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, Returned>>;
@ -159,11 +167,13 @@ export function createDataLoadingThunk<
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
loadData: LoadData<Args, LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<Args, LoadDataResult, Returned>,
maybeThunkOptions?: AppThunkOptions,
) {
let onData: OnData<LoadDataResult, Returned> | undefined;
let onData: OnData<Args, LoadDataResult, Returned> | undefined;
let thunkOptions: AppThunkOptions | undefined;
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
@ -177,7 +187,10 @@ export function createDataLoadingThunk<
return createThunk<Args, Returned>(
name,
async (arg, { getState, dispatch }) => {
const data = await loadData(arg);
const data = await loadData(arg, {
dispatch,
getState,
});
if (!onData) return data as Returned;
@ -185,6 +198,7 @@ export function createDataLoadingThunk<
dispatch,
getState,
discardLoadData: discardLoadDataInPayload,
actionArg: arg,
});
// if there is no return in `onData`, we return the `onData` result

View file

@ -22,12 +22,4 @@ class Admin::Metrics::Measure::ActiveUsersMeasure < Admin::Metrics::Measure::Bas
def activity_tracker
@activity_tracker ||= ActivityTracker.new('activity:logins', :unique)
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
end

View file

@ -86,11 +86,11 @@ class Admin::Metrics::Measure::BaseMeasure
end
def time_period
(@start_at..@end_at)
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at - length_of_period)..(@end_at - length_of_period))
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def length_of_period

View file

@ -48,14 +48,6 @@ class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure
SQL
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -49,14 +49,6 @@ class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measur
SQL
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -49,14 +49,6 @@ class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure:
SQL
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -58,14 +58,6 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
SQL
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -49,14 +49,6 @@ class Admin::Metrics::Measure::InstanceReportsMeasure < Admin::Metrics::Measure:
SQL
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -58,14 +58,6 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:domain, :include_subdomains)
end

View file

@ -22,12 +22,4 @@ class Admin::Metrics::Measure::InteractionsMeasure < Admin::Metrics::Measure::Ba
def activity_tracker
@activity_tracker ||= ActivityTracker.new('activity:interactions', :basic)
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
end

View file

@ -18,7 +18,7 @@ module Admin::Metrics::Measure::QueryHelper
def generated_series_days
Arel.sql(
<<~SQL.squish
SELECT generate_series(timestamp :start_at, :end_at, '1 day')::date AS period
SELECT generate_series(:start_at::timestamp, :end_at::timestamp, '1 day')::date AS period
SQL
)
end

View file

@ -27,14 +27,6 @@ class Admin::Metrics::Measure::TagAccountsMeasure < Admin::Metrics::Measure::Bas
@tag ||= Tag.find(params[:id])
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:id)
end

View file

@ -27,14 +27,6 @@ class Admin::Metrics::Measure::TagUsesMeasure < Admin::Metrics::Measure::BaseMea
@tag ||= Tag.find(params[:id])
end
def time_period
(@start_at.to_date..@end_at.to_date)
end
def previous_time_period
((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period))
end
def params
@params.permit(:id)
end

View file

@ -2,13 +2,8 @@
class CacheBuster
def initialize(options = {})
Rails.application.deprecators[:mastodon].warn('Default values for the cache buster secret header name and values will be removed in Mastodon 4.3. Please set them explicitely if you rely on those.') unless options[:http_method] || (options[:secret] && options[:secret_header])
@secret_header = options[:secret_header] ||
(options[:http_method] ? nil : 'Secret-Header')
@secret = options[:secret] ||
(options[:http_method] ? nil : 'True')
@secret_header = options[:secret_header]
@secret = options[:secret]
@http_method = options[:http_method] || 'GET'
end

View file

@ -86,10 +86,6 @@ module Extractor
possible_entries
end
def extract_cashtags_with_indices(_text)
[]
end
def extract_extra_uris_with_indices(text)
return [] unless text&.index(':')

View file

@ -269,16 +269,21 @@ class LinkDetailsExtractor
end
def document
@document ||= Nokogiri::HTML(@html, nil, encoding)
@document ||= detect_encoding_and_parse_document
end
def encoding
@encoding ||= begin
guess = detector.detect(@html, @html_charset)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil
def detect_encoding_and_parse_document
[detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding|
document = Nokogiri::HTML(@html, nil, encoding)
return document if document.to_s.valid_encoding?
end
end
def detect_encoding
guess = detector.detect(@html, @html_charset)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil
end
def detector
@detector ||= CharlockHolmes::EncodingDetector.new.tap do |detector|
detector.strip_tags = true

View file

@ -3,13 +3,16 @@
class NotificationGroup < ActiveModelSerializers::Model
attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id
def self.from_notification(notification)
def self.from_notification(notification, max_id: nil)
if notification.group_key.present?
# TODO: caching and preloading
most_recent_notifications = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).take(3)
scope = notification.account.notifications.where(group_key: notification.group_key)
scope = scope.where(id: ..max_id) if max_id.present?
most_recent_notifications = scope.order(id: :desc).take(3)
most_recent_id = most_recent_notifications.first.id
sample_accounts = most_recent_notifications.map(&:from_account)
notifications_count = notification.account.notifications.where(group_key: notification.group_key).count
notifications_count = scope.count
else
most_recent_id = notification.id
sample_accounts = [notification.from_account]
@ -29,5 +32,6 @@ class NotificationGroup < ActiveModelSerializers::Model
:target_status,
:report,
:account_relationship_severance_event,
:account_warning,
to: :notification, prefix: false
end

View file

@ -2,7 +2,7 @@
class ActivityPub::OutboxSerializer < ActivityPub::CollectionSerializer
def self.serializer_for(model, options)
if model.class.name == 'ActivityPub::ActivityPresenter'
if model.instance_of?(::ActivityPub::ActivityPresenter)
ActivityPub::ActivitySerializer
else
super

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true
class REST::NotificationPolicySerializer < ActiveModel::Serializer
# Please update `app/javascript/mastodon/api_types/notification_policies.ts` when making changes to the attributes
attributes :filter_not_following,
:filter_not_followers,
:filter_new_accounts,

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class REST::NotificationSerializer < ActiveModel::Serializer
attributes :id, :type, :created_at
attributes :id, :type, :created_at, :group_key
belongs_to :from_account, key: :account, serializer: REST::AccountSerializer
belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
@ -15,6 +15,10 @@ class REST::NotificationSerializer < ActiveModel::Serializer
object.id.to_s
end
def group_key
object.group_key || "ungrouped-#{object.id}"
end
def status_type?
[:favourite, :emoji_reaction, :reaction, :reblog, :status_reference, :status, :list_status, :mention, :poll, :update].include?(object.type)
end

View file

@ -49,6 +49,7 @@ require_relative '../lib/webpacker/manifest_extensions'
require_relative '../lib/webpacker/helper_extensions'
require_relative '../lib/rails/engine_extensions'
require_relative '../lib/action_dispatch/remote_ip_extensions'
require_relative '../lib/stoplight/redis_data_store_extensions'
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/active_record/with_recursive'

View file

@ -135,6 +135,7 @@ et:
media: Lisatud meedia
mutes: Vaigistused
notifications: Teavitused
profile: Sinu Mastodoni profiil
push: Tõuketeated
reports: Teavitused
search: Otsing
@ -165,6 +166,7 @@ et:
admin:write:reports: teostada moderaatori tegevusi teavitustel
crypto: kasuta otspunktkrüpeerimist
follow: muuta kontode suhteid
profile: loe vaid oma konto profiili infot
push: saab tõuketeateid
read: lugeda konto kõiki andmeid
read:accounts: näha konto informatsiooni

View file

@ -285,6 +285,7 @@ et:
update_custom_emoji_html: "%{name} uuendas emotikoni %{target}"
update_domain_block_html: "%{name} uuendas domeeni %{target} keeldu"
update_ip_block_html: "%{name} muutis IP-aadressi %{target} reeglit"
update_report_html: "%{name} uuendas raportit %{target}"
update_status_html: "%{name} muutis %{target} postitust"
update_user_role_html: "%{name} muutis %{target} rolli"
deleted_account: kustutatud konto
@ -292,6 +293,7 @@ et:
filter_by_action: Filtreeri tegevuse järgi
filter_by_user: Filtreeri kasutaja järgi
title: Auditilogi
unavailable_instance: "(domeeni nimi pole saadaval)"
announcements:
destroyed_msg: Teadaande kustutamine õnnestus!
edit:
@ -751,6 +753,7 @@ et:
desc_html: See tugineb välistele hCaptcha skriptidele, mis võib olla turvalisuse ja privaatsuse probleem. Lisaks <strong>võib see muuta registreerimisprotsessi mõnede inimeste (eriti puudega inimeste) jaoks, oluliselt vähem ligipääsetavaks.</strong> Neil põhjustel kaalu palun teisi võimalusi, näiteks kinnitamis- või kutsepõhiseid registreerimislahendusi.
title: Nõua uutelt kasutajatelt konto kinnitamiseks CAPTCHA lahendamist
content_retention:
danger_zone: Ohutsoon
preamble: Määra, kuidas kasutajate loodud sisu Mastodonis talletatakse.
title: Sisu talletamine
default_noindex:
@ -770,7 +773,7 @@ et:
disabled: Mitte kellelegi
users: Sisseloginud kohalikele kasutajatele
registrations:
moderation_recommandation: Enne registreeringute avamist kõigile veendu, et oleks olemas adekvaatne ja reageerimisvalmis modereerijaskond!
moderation_recommandation: Enne kõigi jaoks registreerimise avamist veendu, et oleks olemas adekvaatne ja reageerimisvalmis modereerijaskond!
preamble: Kes saab serveril konto luua.
title: Registreerimised
registrations_mode:
@ -791,7 +794,7 @@ et:
software_updates:
critical_update: Kriitiline — uuenda kiiresti
description: On soovitav hoida oma Mastodoni paigaldus kõige uuemal kujul, et saada kasu kõige värskematest parandustest ja oskustest. Lisaks, vahel on Mastodoni kiire uuendamine kriitiline, et vältida turvaprobleeme. Neil põhjustel kontrollib Mastodon uuendusi iga 30 minuti tagant ja teavitab sind vastavalt su e-posti teavituste eelistustele.
documentation_link: Saa rohkem teada
documentation_link: Vaata lisa
release_notes: Väljalaskemärkused
title: Saadaval uuendused
type: Tüüp
@ -949,6 +952,7 @@ et:
delete: Kustuta
edit_preset: Hoiatuse eelseadistuse muutmine
empty: Hoiatuste eelseadeid pole defineeritud.
title: Hoiatuste eelhäälestused
webhooks:
add_new: Lisa lõpp-punkt
delete: Kustuta
@ -1814,8 +1818,8 @@ et:
title: Arhiivi väljavõte
failed_2fa:
details: 'Sisenemise üksikasjad:'
explanation: Keegi püüdis Su kontole siseneda, ent sisestas vale teisese autentimisfaktori.
further_actions_html: Kui see polnud Sina, siis soovitame viivitamata %{action}, kuna see võib olla lekkinud.
explanation: Keegi püüdis Su kontole siseneda, ent sisestas vale kaheastmelise autentimise faktori.
further_actions_html: Kui see polnud Sina, siis soovitame viivitamata %{action}, kuna see võib olla ohus.
subject: Kaheastmelise autentimise nurjumine
title: Kaheastmeline autentimine nurjus
suspicious_sign_in:
@ -1857,9 +1861,9 @@ et:
silence: Konto limiteeritud
suspend: Konto kustutatud
welcome:
apps_android_action: Google Play poest
apps_android_action: Laadi see Google Playst
apps_ios_action: Allalaadimine App Store'ist
apps_step: Meie ametlikud rakendused.
apps_step: Laadi meie ametlikud rakendused.
apps_title: Mastodoni rakendused
checklist_subtitle: 'Kuidas sel uudsel sotsiaalmeediarindel pihta hakata:'
checklist_title: Millest alustada
@ -1872,7 +1876,7 @@ et:
feature_audience_title: Kogu enesekindlalt jälgijaid
feature_control: Tead ise kõige paremini, mida soovid oma koduvoos näha. Ei aega raiskavaid algoritme ega reklaame. Jälgi ühe kasutajakonto kaudu keda iganes mistahes Mastodoni serveris ja näe postitusi ajalises järjestuses, muutes oma nurgakese Internetist rohkem endale meelepärasemaks.
feature_control_title: Säilita oma ajajoone üle kontroll
feature_creativity: Mastodon toetab audiot, video- ja pildipostitusi, liigipääsetavuse kirjeldusi, küsitlusi, sisuhoiatusi, animeeritud avatare, kohandatud emotikone, pisipiltide lõikeeelistusi ja enamatki, et end võrgus väljendada. Kas avaldad kunsti, muusikat või taskuhäälingusaadet, Mastodon on mõeldud Sinu jaoks.
feature_creativity: Mastodon toetab audiot, video- ja pildipostitusi, ligipääsetavuse kirjeldusi, küsitlusi, sisuhoiatusi, animeeritud avatare, kohandatud emotikone, pisipiltide lõikeeelistusi ja enamatki, et end võrgus väljendada. Kas avaldad kunsti, muusikat või taskuhäälingut, Mastodon on mõeldud Sinu jaoks.
feature_creativity_title: Võrreldamatu loovus
feature_moderation: Mastodon annab otsustusõiguse tagasi Sinu kätte. Igal serveril on oma reeglid ja regulatsioonid, mida hallatakse kohapeal, mitte nagu ülalt-alla korporatiivses sotsiaalmeedias, võimaldades enim paindlikku vastavust erinevate vajadustega gruppide ja inimeste eelistustele. Liitu sobivate reeglitega serveriga, või käivita oma server.
feature_moderation_title: Modereerimine, nagu see olema peab

View file

@ -77,10 +77,15 @@ et:
warn: Varja filtreeritud sisu hoiatusega, nimetades filtri pealkirja
form_admin_settings:
activity_api_enabled: Kohalike postituste, aktiivsete kasutajate ja uute registreerumistr arv nädala kaupa grupeeritult
app_icon: WEBP, PNG, GIF või JPG. Asendab mobiilsel seadmel äpi vaikeikooni kohandatud ikooniga.
backups_retention_period: Kasutajatel on võimalus genereerida oma postitustest hiljem allalaaditav arhiiv. Kui määrata positiivne arv, kustutatakse serveri talletusruumist need arhiivid määratud arvu päevade järel automaatselt.
bootstrap_timeline_accounts: Need kasutajad kinnitatakse uute kasutajate jälgimissoovituste esiritta.
closed_registrations_message: Kuvatakse, kui liitumised pole võimalikud
custom_css: Kohandatud stiile on võimalik kasutada Mastodoni veebiliideses.
content_cache_retention_period: Kõik teiste serverite postitused (sealhulgas jagamised ja vastused) kustutatakse pärast määratud arvu päevade möödumist, sõltumata, kuidas kohalik kasutaja on nende postitustega interakteerunud. Hõlmatud on ka postitused, mille kohalik kasutaja on märkinud järjehoidjaks või lemmikuks. Ka eri instantside kasutajate vahelised privaatsed mainimised kaovad ja neid on võimatu taastada. See seadistus on mõeldud eriotstarbeliste instantside jaoks ja rikub paljude kasutajate ootusi, kui seda rakendatakse üldotstarbelise kasutuse puhul.
custom_css: Mastodoni veebiliideses on võimalik kasutada kohandatud stiile.
favicon: WEBP, PNG, GIF või JPG. Asendab Mastodoni vaike- favicon ikooni kohandatud ikooniga.
mascot: Asendab kohandatud veebiliidese illustratsiooni.
media_cache_retention_period: Kaugkasutajate tehtud postituste meediafailid salvestatakse teie serveri vahemällu. Kui see seadistus on seatud positiivsele väärtusele, kustutatakse meediumifailid määratud päevade möödumisel. Kui meediaandmeid küsitakse pärast nende kustutamist, laaditakse need uuesti alla, kui lähtesisu on veel saadaval. Kuna on olemas piirangud, kui tihti tohivad lingikaardid kolmandatelt saitidelt andmeid pärida, on soovitatav määrata väärtuseks vähemalt 14. Vastasel juhul ei uuendata linkide eelvaatekaarte nõudmise korral enne seda aega.
peers_api_enabled: Domeeninimede loetelu, mida see server on Fediversumis kohanud. Mitte mingeid andmeid selle serveri födereerumise kohta antud serverite pole, vaid üksnes info, et sellest serverist ollakse teadlik. Seda kasutavad teenused, mis koguvad üldist födereerumise statistikat.
profile_directory: Kasutajate kataloog kuvab nimekirja kasutajatest, kes on seda lubanud.
require_invite_text: Kui liitumisi on tarvis kinnitada, oleks "Miks soovid liituda?" vastus vajalik
@ -240,6 +245,7 @@ et:
backups_retention_period: Kasutajate arhiivi talletusperiood
bootstrap_timeline_accounts: Alati soovita neid kontosid uutele kasutajatele
closed_registrations_message: Kohandatud teade, kui liitumine pole võimalik
content_cache_retention_period: Kaugsisu säilitamise aeg
custom_css: Kohandatud CSS
mascot: Kohandatud maskott (kunagine)
media_cache_retention_period: Meediapuhvri talletusperiood

View file

@ -293,6 +293,7 @@ sv:
filter_by_action: Filtrera efter åtgärd
filter_by_user: Filtrera efter användare
title: Revisionslogg
unavailable_instance: "(domännamn inte tillgängligt)"
announcements:
destroyed_msg: Kungörelsen raderades!
edit:

View file

@ -1791,8 +1791,8 @@ vi:
suspicious_sign_in:
change_password: đổi mật khẩu của bạn
details: 'Chi tiết thông tin đăng nhập:'
explanation: Chúng tôi phát hiện lần đăng nhập bất thường tài khoản của bạn từ một địa chỉ IP mới.
further_actions_html: Nếu đó không phải là bạn, chúng tôi khuyến nghị %{action} lập tức và bật xác minh hai bước để giữ tài khoản được an toàn.
explanation: Chúng tôi phát hiện tài khoản của bạn đăng nhập bất thường từ một địa chỉ IP mới.
further_actions_html: Nếu đây không phải là bạn, hãy %{action} lập tức và bật xác minh hai bước để giữ tài khoản được an toàn.
subject: Đăng nhập tài khoản từ địa chỉ IP mới
title: Lần đăng nhập mới
warning:

View file

@ -1,10 +1,10 @@
This privacy policy describes how %{domain}s ("%{domain}s", "we", "us")
collects, protects and uses the personally identifiable information you may
provide through the %{domain}s website or its API. The policy also
describes the choices available to you regarding our use of your personal
information and how you can access and update this information. This policy
does not apply to the practices of companies that %{domain}s does not own
or control, or to individuals that %{domain}s does not employ or manage.
This privacy policy describes how %{domain} ("%{domain}", "we", "us") collects,
protects and uses the personally identifiable information you may provide
through the %{domain} website or its API. The policy also describes the choices
available to you regarding our use of your personal information and how you can
access and update this information. This policy does not apply to the practices
of companies that %{domain} does not own or control, or to individuals that
%{domain} does not employ or manage.
# What information do we collect?

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AddAttachmentAvatarToAccounts < ActiveRecord::Migration[4.2]
def self.up
def up
change_table :accounts do |t|
# The following corresponds to `t.attachment :avatar` in an older version of Paperclip
t.string :avatar_file_name
@ -11,7 +11,7 @@ class AddAttachmentAvatarToAccounts < ActiveRecord::Migration[4.2]
end
end
def self.down
def down
remove_attachment :accounts, :avatar
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AddDeviseToUsers < ActiveRecord::Migration[4.2]
def self.up
def up
change_table(:users, bulk: true) do |t|
## Database authenticatable
t.string :encrypted_password, null: false, default: ''
@ -24,7 +24,7 @@ class AddDeviseToUsers < ActiveRecord::Migration[4.2]
add_index :users, :reset_password_token, unique: true
end
def self.down
def down
remove_index :users, :reset_password_token
remove_column :users, :encrypted_password

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AddAttachmentHeaderToAccounts < ActiveRecord::Migration[4.2]
def self.up
def up
change_table :accounts do |t|
# The following corresponds to `t.attachment :header` in an older version of Paperclip
t.string :header_file_name
@ -11,7 +11,7 @@ class AddAttachmentHeaderToAccounts < ActiveRecord::Migration[4.2]
end
end
def self.down
def down
remove_attachment :accounts, :header
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class RailsSettingsMigration < ActiveRecord::Migration[5.0]
def self.up
def up
create_table :settings do |t|
t.string :var, null: false
t.text :value
@ -11,7 +11,7 @@ class RailsSettingsMigration < ActiveRecord::Migration[5.0]
add_index :settings, [:target_type, :target_id, :var], unique: true
end
def self.down
def down
drop_table :settings
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AddAttachmentDataToImports < ActiveRecord::Migration[4.2]
def self.up
def up
change_table :imports do |t|
# The following corresponds to `t.attachment :data` in an older version of Paperclip
t.string :data_file_name
@ -11,7 +11,7 @@ class AddAttachmentDataToImports < ActiveRecord::Migration[4.2]
end
end
def self.down
def down
remove_attachment :imports, :data
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
# Restore compatibility with Redis < 6.2
module Stoplight
module DataStore
module RedisExtensions
def query_failures(light, transaction: @redis)
window_start = Time.now.to_i - light.window_size
transaction.zrevrangebyscore(failures_key(light), Float::INFINITY, window_start)
end
end
end
end
Stoplight::DataStore::Redis.prepend(Stoplight::DataStore::RedisExtensions)

View file

@ -1,7 +1,7 @@
{
"name": "@mastodon/mastodon",
"license": "AGPL-3.0-or-later",
"packageManager": "yarn@4.3.0",
"packageManager": "yarn@4.3.1",
"engines": {
"node": ">=18"
},

View file

@ -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=ISO-8859-1
content-length: 158
accept-ranges: bytes
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tofu á l'orange</title>
</head>
<body>
<h2>Tofu á l'orange</h2>
</body>
</html>

View file

@ -28,14 +28,6 @@ describe CacheBuster do
end
context 'when using default options' do
around do |example|
# Disables the CacheBuster.new deprecation warning about default arguments.
# Remove this `silence` block when default arg support is removed from CacheBuster
Rails.application.deprecators[:mastodon].silence do
example.run
end
end
include_examples 'makes_request'
end

View file

@ -69,10 +69,10 @@ describe Extractor do
end
end
describe 'extract_cashtags_with_indices' do
it 'returns []' do
describe 'extract_entities_with_indices' do
it 'returns empty array when cashtag present' do
text = '$cashtag'
extracted = described_class.extract_cashtags_with_indices(text)
extracted = described_class.extract_entities_with_indices(text)
expect(extracted).to eq []
end
end

View file

@ -7,11 +7,28 @@ describe 'Admin Measures' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:account) { Fabricate(:account) }
let(:params) do
{
keys: %w(instance_accounts instance_follows instance_followers),
instance_accounts: {
domain: 'mastodon.social',
include_subdomains: true,
},
instance_follows: {
domain: 'mastodon.social',
include_subdomains: true,
},
instance_followers: {
domain: 'mastodon.social',
include_subdomains: true,
},
}
end
describe 'GET /api/v1/admin/measures' do
context 'when not authorized' do
it 'returns http forbidden' do
post '/api/v1/admin/measures', params: { account_id: account.id, limit: 2 }
post '/api/v1/admin/measures', params: params
expect(response)
.to have_http_status(403)
@ -22,7 +39,7 @@ describe 'Admin Measures' do
let(:scopes) { 'admin:read' }
it 'returns http success and status json' do
post '/api/v1/admin/measures', params: { account_id: account.id, limit: 2 }, headers: headers
post '/api/v1/admin/measures', params: params, headers: headers
expect(response)
.to have_http_status(200)

View file

@ -28,6 +28,7 @@ RSpec.describe FetchLinkCardService do
stub_request(:get, 'http://example.com/sjis_with_wrong_charset').to_return(request_fixture('sjis_with_wrong_charset.txt'))
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'))
Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache
@ -150,6 +151,14 @@ RSpec.describe FetchLinkCardService do
end
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') }
it 'decodes the HTML' do
expect(status.preview_card.title).to eq("Tofu á l'orange")
end
end
context 'with a Japanese path URL' do
let(:status) { Fabricate(:status, text: 'テストhttp://example.com/日本語') }

View file

@ -6,3 +6,14 @@ RSpec::Matchers.define :match_json_schema do |schema|
JSON::Validator.validate(schema_path, input_json, validate_schema: true)
end
end
RSpec::Matchers.define :match_json_values do |values|
match do |string|
expect(json_str_to_hash(string))
.to include(values)
end
failure_message do |value|
"expected that #{value} would have the same values as #{values}."
end
end

View file

@ -80,9 +80,6 @@ end
RSpec.configure do |config|
config.before :suite do
if streaming_examples_present?
# Compile assets
Webpacker.compile
# Start the node streaming server
streaming_server_manager.start(port: STREAMING_PORT)
end

View file

@ -16,7 +16,7 @@ describe ActivityPub::DistributePollUpdateWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), account.id, 'http://example.com']]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Update'), account.id, 'http://example.com']]) do
subject.perform(status.id)
end
end

View file

@ -19,7 +19,7 @@ describe ActivityPub::DistributionWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), status.account.id, 'http://example.com', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Create'), status.account.id, 'http://example.com', anything]]) do
subject.perform(status.id)
end
end
@ -43,7 +43,7 @@ describe ActivityPub::DistributionWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), status.account.id, 'http://example.com', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Create'), status.account.id, 'http://example.com', anything]]) do
subject.perform(status.id)
end
end
@ -104,7 +104,7 @@ describe ActivityPub::DistributionWorker do
end
it 'delivers to mentioned accounts' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), status.account.id, 'https://foo.bar/inbox', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Create'), status.account.id, 'https://foo.bar/inbox', anything]]) do
subject.perform(status.id)
end
end

View file

@ -16,12 +16,16 @@ describe ActivityPub::MoveDistributionWorker do
end
it 'delivers to followers and known blockers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [
[kind_of(String), migration.account.id, 'http://example.com'],
[kind_of(String), migration.account.id, 'http://example2.com'],
]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, expected_migration_deliveries) do
subject.perform(migration.id)
end
end
def expected_migration_deliveries
[
[match_json_values(type: 'Move'), migration.account.id, 'http://example.com'],
[match_json_values(type: 'Move'), migration.account.id, 'http://example2.com'],
]
end
end
end

View file

@ -25,7 +25,7 @@ describe ActivityPub::StatusUpdateDistributionWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), status.account.id, 'http://example.com', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Update'), status.account.id, 'http://example.com', anything]]) do
subject.perform(status.id)
end
end
@ -49,7 +49,7 @@ describe ActivityPub::StatusUpdateDistributionWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), status.account.id, 'http://example.com', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Update'), status.account.id, 'http://example.com', anything]]) do
subject.perform(status.id)
end
end

View file

@ -14,7 +14,7 @@ describe ActivityPub::UpdateDistributionWorker do
end
it 'delivers to followers' do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[kind_of(String), account.id, 'http://example.com', anything]]) do
expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [[match_json_values(type: 'Update'), account.id, 'http://example.com', anything]]) do
subject.perform(account.id)
end
end

View file

@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1.7
# syntax=docker/dockerfile:1.8
# Please see https://docs.docker.com/engine/reference/builder for information about
# the extended buildx capabilities used in this file.
@ -13,7 +13,14 @@ ARG NODE_MAJOR_VERSION="20"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm"
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as streaming
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS streaming
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.3.0-nightly.2023.11.09+pr-123456
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
ARG MASTODON_VERSION_METADATA=""
# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin]
ARG TZ="Etc/UTC"

View file

@ -1,7 +1,7 @@
{
"name": "@mastodon/streaming",
"license": "AGPL-3.0-or-later",
"packageManager": "yarn@4.3.0",
"packageManager": "yarn@4.3.1",
"engines": {
"node": ">=18"
},
@ -34,7 +34,7 @@
"@types/cors": "^2.8.16",
"@types/express": "^4.17.17",
"@types/pg": "^8.6.6",
"@types/uuid": "^9.0.0",
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.9",
"eslint-define-config": "^2.0.0",
"pino-pretty": "^11.0.0",

235
yarn.lock
View file

@ -2944,7 +2944,7 @@ __metadata:
"@types/cors": "npm:^2.8.16"
"@types/express": "npm:^4.17.17"
"@types/pg": "npm:^8.6.6"
"@types/uuid": "npm:^9.0.0"
"@types/uuid": "npm:^10.0.0"
"@types/ws": "npm:^8.5.9"
bufferutil: "npm:^4.0.7"
cors: "npm:^2.8.5"
@ -4064,10 +4064,10 @@ __metadata:
languageName: node
linkType: hard
"@types/uuid@npm:^9.0.0":
version: 9.0.8
resolution: "@types/uuid@npm:9.0.8"
checksum: 10c0/b411b93054cb1d4361919579ef3508a1f12bf15b5fdd97337d3d351bece6c921b52b6daeef89b62340fd73fd60da407878432a1af777f40648cbe53a01723489
"@types/uuid@npm:^10.0.0":
version: 10.0.0
resolution: "@types/uuid@npm:10.0.0"
checksum: 10c0/9a1404bf287164481cb9b97f6bb638f78f955be57c40c6513b7655160beb29df6f84c915aaf4089a1559c216557dc4d2f79b48d978742d3ae10b937420ddac60
languageName: node
linkType: hard
@ -5662,17 +5662,17 @@ __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":
version: 4.23.0
resolution: "browserslist@npm:4.23.0"
"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":
version: 4.23.1
resolution: "browserslist@npm:4.23.1"
dependencies:
caniuse-lite: "npm:^1.0.30001587"
electron-to-chromium: "npm:^1.4.668"
caniuse-lite: "npm:^1.0.30001629"
electron-to-chromium: "npm:^1.4.796"
node-releases: "npm:^2.0.14"
update-browserslist-db: "npm:^1.0.13"
update-browserslist-db: "npm:^1.0.16"
bin:
browserslist: cli.js
checksum: 10c0/8e9cc154529062128d02a7af4d8adeead83ca1df8cd9ee65a88e2161039f3d68a4d40fea7353cab6bae4c16182dec2fdd9a1cf7dc2a2935498cee1af0e998943
checksum: 10c0/eb47c7ab9d60db25ce2faca70efeb278faa7282a2f62b7f2fa2f92e5f5251cf65144244566c86559419ff4f6d78f59ea50e39911321ad91f3b27788901f1f5e9
languageName: node
linkType: hard
@ -5874,10 +5874,10 @@ __metadata:
languageName: node
linkType: hard
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001599":
version: 1.0.30001599
resolution: "caniuse-lite@npm:1.0.30001599"
checksum: 10c0/8b3b9610b5be88533a3c8d0770d6896f7b1a9fee3dbeb7339e4ee119a514c81e5e07a628a5a289a6541ca291ac78a9402f5a99cf6012139e91f379083488a8eb
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001629":
version: 1.0.30001636
resolution: "caniuse-lite@npm:1.0.30001636"
checksum: 10c0/e5f965b4da7bae1531fd9f93477d015729ff9e3fa12670ead39a9e6cdc4c43e62c272d47857c5cc332e7b02d697cb3f2f965a1030870ac7476da60c2fc81ee94
languageName: node
linkType: hard
@ -6751,43 +6751,43 @@ __metadata:
languageName: node
linkType: hard
"cssnano-preset-default@npm:^7.0.2":
version: 7.0.2
resolution: "cssnano-preset-default@npm:7.0.2"
"cssnano-preset-default@npm:^7.0.3":
version: 7.0.3
resolution: "cssnano-preset-default@npm:7.0.3"
dependencies:
browserslist: "npm:^4.23.0"
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.0"
postcss-convert-values: "npm:^7.0.0"
postcss-discard-comments: "npm:^7.0.0"
postcss-colormin: "npm:^7.0.1"
postcss-convert-values: "npm:^7.0.1"
postcss-discard-comments: "npm:^7.0.1"
postcss-discard-duplicates: "npm:^7.0.0"
postcss-discard-empty: "npm:^7.0.0"
postcss-discard-overridden: "npm:^7.0.0"
postcss-merge-longhand: "npm:^7.0.1"
postcss-merge-rules: "npm:^7.0.1"
postcss-merge-longhand: "npm:^7.0.2"
postcss-merge-rules: "npm:^7.0.2"
postcss-minify-font-values: "npm:^7.0.0"
postcss-minify-gradients: "npm:^7.0.0"
postcss-minify-params: "npm:^7.0.0"
postcss-minify-selectors: "npm:^7.0.1"
postcss-minify-params: "npm:^7.0.1"
postcss-minify-selectors: "npm:^7.0.2"
postcss-normalize-charset: "npm:^7.0.0"
postcss-normalize-display-values: "npm:^7.0.0"
postcss-normalize-positions: "npm:^7.0.0"
postcss-normalize-repeat-style: "npm:^7.0.0"
postcss-normalize-string: "npm:^7.0.0"
postcss-normalize-timing-functions: "npm:^7.0.0"
postcss-normalize-unicode: "npm:^7.0.0"
postcss-normalize-unicode: "npm:^7.0.1"
postcss-normalize-url: "npm:^7.0.0"
postcss-normalize-whitespace: "npm:^7.0.0"
postcss-ordered-values: "npm:^7.0.0"
postcss-reduce-initial: "npm:^7.0.0"
postcss-ordered-values: "npm:^7.0.1"
postcss-reduce-initial: "npm:^7.0.1"
postcss-reduce-transforms: "npm:^7.0.0"
postcss-svgo: "npm:^7.0.1"
postcss-unique-selectors: "npm:^7.0.1"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/7c66240594c1d7a0cc761e755236228b17251455aa57abc45be0631f7de0fde070c23b0e41ffa200d39cd8351718514217d8c7a8cc4f06b54289dc1d555dfeb2
checksum: 10c0/ab3e51003efed6542a12d43c10ca693ab26138a1d035697b9be8f07e084e37a78617cbb8028b0a7e7841302ec151f4ecf35cbd763efe291846b62c35ea4c0bb4
languageName: node
linkType: hard
@ -6801,14 +6801,14 @@ __metadata:
linkType: hard
"cssnano@npm:^7.0.0":
version: 7.0.2
resolution: "cssnano@npm:7.0.2"
version: 7.0.3
resolution: "cssnano@npm:7.0.3"
dependencies:
cssnano-preset-default: "npm:^7.0.2"
lilconfig: "npm:^3.1.1"
cssnano-preset-default: "npm:^7.0.3"
lilconfig: "npm:^3.1.2"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/ad43d8c2e96fa1022fc5103064e4f08da3fdc5501a946d455edf0b81981b58cd06ad2d3f0c68d666e2b687c10c02ffbb383252aa34da0ddc3bd4d075f4a922c7
checksum: 10c0/4cbcd1e0ebe0bd83196cc5b16b3a60d3ebc98326c79b2f71df597bb73c8e3ee1f42b89159d7a038acc398251184d648d9dd516f4194e46746f3af6fa74b4aec7
languageName: node
linkType: hard
@ -7423,10 +7423,10 @@ __metadata:
languageName: node
linkType: hard
"electron-to-chromium@npm:^1.4.668":
version: 1.4.681
resolution: "electron-to-chromium@npm:1.4.681"
checksum: 10c0/5b2558dfb8bb82c20fb5fa1d9bbe06a3add47431dc3e1e4815e997be6ad387787047d9e534ed96839a9e7012520a5281c865158b09db41d10c029af003f05f94
"electron-to-chromium@npm:^1.4.796":
version: 1.4.806
resolution: "electron-to-chromium@npm:1.4.806"
checksum: 10c0/44f925ba64bb623d0482cd2f14fcfb27017cec1f11ee642e0d49cb128cd714ba5e074c2cd8756dfc18010aa66d3c64039cdea891905f7c17160a8963db574fab
languageName: node
linkType: hard
@ -7742,10 +7742,10 @@ __metadata:
languageName: node
linkType: hard
"escalade@npm:^3.1.1":
version: 3.1.1
resolution: "escalade@npm:3.1.1"
checksum: 10c0/afd02e6ca91ffa813e1108b5e7756566173d6bc0d1eb951cb44d6b21702ec17c1cf116cfe75d4a2b02e05acb0b808a7a9387d0d1ca5cf9c04ad03a8445c3e46d
"escalade@npm:^3.1.1, escalade@npm:^3.1.2":
version: 3.1.2
resolution: "escalade@npm:3.1.2"
checksum: 10c0/6b4adafecd0682f3aa1cd1106b8fff30e492c7015b178bc81b2d2f75106dabea6c6d6e8508fc491bd58e597c74abb0e8e2368f943ecb9393d4162e3c2f3cf287
languageName: node
linkType: hard
@ -8952,17 +8952,18 @@ __metadata:
linkType: hard
"glob@npm:^10.2.2, glob@npm:^10.2.6, glob@npm:^10.3.10":
version: 10.4.1
resolution: "glob@npm:10.4.1"
version: 10.4.2
resolution: "glob@npm:10.4.2"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^3.1.2"
minimatch: "npm:^9.0.4"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^1.11.1"
bin:
glob: dist/esm/bin.mjs
checksum: 10c0/77f2900ed98b9cc2a0e1901ee5e476d664dae3cd0f1b662b8bfd4ccf00d0edc31a11595807706a274ca10e1e251411bbf2e8e976c82bed0d879a9b89343ed379
checksum: 10c0/2c7296695fa75a935f3ad17dc62e4e170a8bb8752cf64d328be8992dd6ad40777939003754e10e9741ff8fbe43aa52fba32d6930d0ffa0e3b74bc3fb5eebaa2f
languageName: node
linkType: hard
@ -11295,10 +11296,10 @@ __metadata:
languageName: node
linkType: hard
"lilconfig@npm:^3.1.1, lilconfig@npm:~3.1.1":
version: 3.1.1
resolution: "lilconfig@npm:3.1.1"
checksum: 10c0/311b559794546894e3fe176663427326026c1c644145be9e8041c58e268aa9328799b8dfe7e4dd8c6a4ae305feae95a1c9e007db3569f35b42b6e1bc8274754c
"lilconfig@npm:^3.1.2, lilconfig@npm:~3.1.1":
version: 3.1.2
resolution: "lilconfig@npm:3.1.2"
checksum: 10c0/f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe
languageName: node
linkType: hard
@ -12696,6 +12697,13 @@ __metadata:
languageName: node
linkType: hard
"package-json-from-dist@npm:^1.0.0":
version: 1.0.0
resolution: "package-json-from-dist@npm:1.0.0"
checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033
languageName: node
linkType: hard
"pako@npm:~1.0.5":
version: 1.0.11
resolution: "pako@npm:1.0.11"
@ -13264,29 +13272,29 @@ __metadata:
languageName: node
linkType: hard
"postcss-colormin@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-colormin@npm:7.0.0"
"postcss-colormin@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-colormin@npm:7.0.1"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
caniuse-api: "npm:^3.0.0"
colord: "npm:^2.9.3"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/d365a5365e0a94748309d32c7208cd06249bc53eb82cc32c771de4073b109fa8552e58d60dbe84d7e69e68081ed8a01fbf645d38a650e90cb2e13b21043cd796
checksum: 10c0/e01e9e129ce39320cf10bb09ee2a3ab5b44b77805d3f7827eea07297644838b85c33b78996eeae0a1aba327ffd72a6ab1d24949ebc831523e310e916d5500371
languageName: node
linkType: hard
"postcss-convert-values@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-convert-values@npm:7.0.0"
"postcss-convert-values@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-convert-values@npm:7.0.1"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/5d7cfa06f307e024574a1842016f006691e0c1932352f53a99ce8f2f9930c64c3c1ae17518e9e4e5176630b99f1beaab37bc339bc779fb07dc543670ae66bb21
checksum: 10c0/612f025f179f0f2ad7365db8c0b423614dcb8e1e4061875a4691a39dede0bca758d1a8f9f5c8b08e12af053e9e884f65ca5626ccc723d5b3f420650d67fe3046
languageName: node
linkType: hard
@ -13344,12 +13352,14 @@ __metadata:
languageName: node
linkType: hard
"postcss-discard-comments@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-discard-comments@npm:7.0.0"
"postcss-discard-comments@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-discard-comments@npm:7.0.1"
dependencies:
postcss-selector-parser: "npm:^6.1.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/7fef7deea85c1e68161f69057be19a3aedd54d23c9b464c9b1531faa7a115f0c96a4f0ee3a560ce300578599dbc8114fe0fb744208b20b9d2fd8df1b4b39c58a
checksum: 10c0/64de3fced7e0e49dfea6aede8a5ca495b81dcd01c3c47734a5fbc86a3cb2b1ae39e1958e34e9bea64ecabd5fb4d8c287ec1d6189cf175c2434c6b6582da79948
languageName: node
linkType: hard
@ -13494,29 +13504,29 @@ __metadata:
languageName: node
linkType: hard
"postcss-merge-longhand@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-merge-longhand@npm:7.0.1"
"postcss-merge-longhand@npm:^7.0.2":
version: 7.0.2
resolution: "postcss-merge-longhand@npm:7.0.2"
dependencies:
postcss-value-parser: "npm:^4.2.0"
stylehacks: "npm:^7.0.1"
stylehacks: "npm:^7.0.2"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/e3d20502e65c82c9c4ba2e400bd093ee6b9c1b0019618ccd50eb40ef0e496206dd518c7e655a6986d780d5a52576e32e8f310d00484b15f67c77664a148df6eb
checksum: 10c0/71d22418e5850e134bfcf2038acb1c91eeb48d9b6f9e4c0252fee757e11dc4ae351097a855c327fbc38dc7658fa6edc7cc7f458c08bb4044b82c6005dc565fcb
languageName: node
linkType: hard
"postcss-merge-rules@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-merge-rules@npm:7.0.1"
"postcss-merge-rules@npm:^7.0.2":
version: 7.0.2
resolution: "postcss-merge-rules@npm:7.0.2"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
caniuse-api: "npm:^3.0.0"
cssnano-utils: "npm:^5.0.0"
postcss-selector-parser: "npm:^6.1.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/d380c162327e7aad59efb55cfddc5ec4e3bf51d18b07b832fdd876505279bac3cb44511ada8e1e1992428dcec4f64c7ec457b6ff9109063c5a61abf4b59b7176
checksum: 10c0/fbad20382ca45f1b3b5ff704c075f899cc9ba8418ae6effbdeb9e7c1f9b5c24996d1941ad36cd0936d60cbf127a72f235b2cbb0c44d9239a8a61042406d95b4a
languageName: node
linkType: hard
@ -13544,27 +13554,28 @@ __metadata:
languageName: node
linkType: hard
"postcss-minify-params@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-minify-params@npm:7.0.0"
"postcss-minify-params@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-minify-params@npm:7.0.1"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
cssnano-utils: "npm:^5.0.0"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/28a7ae313a197aeaff8b3fa1e695a6443b11a74258374a05adee6a1b05f5849ef52037b7a5069d6910614b03b4610acdaf4a76f38b89cb42e813a8cb5ec2fc01
checksum: 10c0/84e1c4de5af47178c1f48e10ce16341560f32bb2becf5ec9b014103f6c706174bc70885e2e13ced4de9b4f1959fb970193261cbd4751c8b87d5159e859e8c5ec
languageName: node
linkType: hard
"postcss-minify-selectors@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-minify-selectors@npm:7.0.1"
"postcss-minify-selectors@npm:^7.0.2":
version: 7.0.2
resolution: "postcss-minify-selectors@npm:7.0.2"
dependencies:
cssesc: "npm:^3.0.0"
postcss-selector-parser: "npm:^6.1.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/a8ff69657fb1808d8f0f105b13a416426902d6f498a4b7ebb3b96b4b9149e97ee2e2ad6cd98108e2f0b8f781701724e6c51e120e215cee3e40c2d7a2afac755a
checksum: 10c0/87e0c21a0135b6c61b58d62c4c1e0cbd3cfb516ff8105db714c6a33a5edc477846ae220399d368e4ef6518529c711aa2dee9ff49e9befd93e83d5c939f3084a1
languageName: node
linkType: hard
@ -13689,15 +13700,15 @@ __metadata:
languageName: node
linkType: hard
"postcss-normalize-unicode@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-normalize-unicode@npm:7.0.0"
"postcss-normalize-unicode@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-normalize-unicode@npm:7.0.1"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/f2d6ab0076c006dcf3ed33ba30686f2d29e81a408c66acced22e2c942df6d613697ea786137833dd258aafab5fda4d3eb27df13a82df830357dbad9b79154881
checksum: 10c0/fcabac738765f608cb211ac01a19f9a7d784f99d223ef6f31352f37b9bcf008e0ca7849414c9e77116be8660d78b01dba6fc4a87cee7c9a977d3c2741d6a222d
languageName: node
linkType: hard
@ -13732,15 +13743,15 @@ __metadata:
languageName: node
linkType: hard
"postcss-ordered-values@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-ordered-values@npm:7.0.0"
"postcss-ordered-values@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-ordered-values@npm:7.0.1"
dependencies:
cssnano-utils: "npm:^5.0.0"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/42b14f9518b573318594c2aeb2f13fd1fbe44936d14f1b28a438e7a82644ace9a2946699bebfe7a2d383534dc24e7203c35308d749f3c585a86daa238ad920a4
checksum: 10c0/9fc62e9039c7d4fa417d165678b065fc577a7232aa41a94a4e9208ad7db2268e1ce003aaad7c6a569afdf890a43416b0bf21047461505b4e3a16eec311a6eb63
languageName: node
linkType: hard
@ -13856,15 +13867,15 @@ __metadata:
languageName: node
linkType: hard
"postcss-reduce-initial@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-reduce-initial@npm:7.0.0"
"postcss-reduce-initial@npm:^7.0.1":
version: 7.0.1
resolution: "postcss-reduce-initial@npm:7.0.1"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
caniuse-api: "npm:^3.0.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/ed50cd680ce258df953b82ce9b3fb52564d08548724577810800e236d017d80430cbccb4b1ad38b0f4d521663598e44ab93136b20064231181ef49e1e113ae10
checksum: 10c0/3e7c6c1d0cc4ace38f44971f02d0dd98131af678cf114aa05cfa0c066420019d5329c221ef8507644b8a79d0e76534303f747c97860ac777fe17f57110edefe1
languageName: node
linkType: hard
@ -15422,15 +15433,15 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
version: 1.77.5
resolution: "sass@npm:1.77.5"
version: 1.77.6
resolution: "sass@npm:1.77.6"
dependencies:
chokidar: "npm:>=3.0.0 <4.0.0"
immutable: "npm:^4.0.0"
source-map-js: "npm:>=0.6.2 <2.0.0"
bin:
sass: sass.js
checksum: 10c0/9da049b0a3fadab419084d6becdf471e107cf6e3c8ac87cabea2feb845afac75e86c99e06ee721a5aa4f6a2d833ec5380137c4e540ab2f760edf1e4eb6139e69
checksum: 10c0/fe5a393c0aa29eda9f83c06be9b94788b61fe8bad0616ee6e3a25d21ab504f430d40c0064fdca89b02b8e426411ae6dcd906c91f2e48c263575c3d392b6daeb1
languageName: node
linkType: hard
@ -16474,15 +16485,15 @@ __metadata:
languageName: node
linkType: hard
"stylehacks@npm:^7.0.1":
version: 7.0.1
resolution: "stylehacks@npm:7.0.1"
"stylehacks@npm:^7.0.2":
version: 7.0.2
resolution: "stylehacks@npm:7.0.2"
dependencies:
browserslist: "npm:^4.23.0"
browserslist: "npm:^4.23.1"
postcss-selector-parser: "npm:^6.1.0"
peerDependencies:
postcss: ^8.4.31
checksum: 10c0/538d5d9c6d84906efad3706f0873b85b67fa224f17759b122bad3d60f2928c31204fd658dd16ec952bf54858a3aeaef4643e040c04030459285ce1b13c4cae91
checksum: 10c0/552299e869ac430e1076c025ace6c80bdfd5b672632435625eb1bd45ff5611aad7f3ee810e72edc04dc0fc3761e08dec40d27b72c50b95690eea2de43238b8d8
languageName: node
linkType: hard
@ -17408,17 +17419,17 @@ __metadata:
languageName: node
linkType: hard
"update-browserslist-db@npm:^1.0.13":
version: 1.0.13
resolution: "update-browserslist-db@npm:1.0.13"
"update-browserslist-db@npm:^1.0.16":
version: 1.0.16
resolution: "update-browserslist-db@npm:1.0.16"
dependencies:
escalade: "npm:^3.1.1"
picocolors: "npm:^1.0.0"
escalade: "npm:^3.1.2"
picocolors: "npm:^1.0.1"
peerDependencies:
browserslist: ">= 4.21.0"
bin:
update-browserslist-db: cli.js
checksum: 10c0/e52b8b521c78ce1e0c775f356cd16a9c22c70d25f3e01180839c407a5dc787fb05a13f67560cbaf316770d26fa99f78f1acd711b1b54a4f35d4820d4ea7136e6
checksum: 10c0/5995399fc202adbb51567e4810e146cdf7af630a92cc969365a099150cb00597e425cc14987ca7080b09a4d0cfd2a3de53fbe72eebff171aed7f9bb81f9bf405
languageName: node
linkType: hard