mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-05 17:31:12 +00:00
Merge branch 'main' into feature/require-mfa-by-admin
This commit is contained in:
commit
d114645e60
1
.github/workflows/build-security.yml
vendored
1
.github/workflows/build-security.yml
vendored
|
@ -9,7 +9,6 @@ permissions:
|
|||
jobs:
|
||||
compute-suffix:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'mastodon/mastodon'
|
||||
steps:
|
||||
- id: version_vars
|
||||
env:
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.10.2'
|
||||
const PACKAGE_VERSION = '2.10.4'
|
||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
|
|
@ -583,7 +583,6 @@ The following changelog entries focus on changes visible to users, administrator
|
|||
You can now separately filter or drop notifications from people you don't follow, people who don't follow you, accounts created within the past 30 days, as well as unsolicited private mentions, and accounts limited by the moderation.\
|
||||
Instead of being outright dropped, notifications that you chose to filter are put in a separate “Filtered notifications” box that you can review separately without it clogging your main notifications.\
|
||||
This adds the following REST API endpoints:
|
||||
|
||||
- `GET /api/v2/notifications/policy`: https://docs.joinmastodon.org/methods/notifications/#get-policy
|
||||
- `PATCH /api/v2/notifications/policy`: https://docs.joinmastodon.org/methods/notifications/#update-the-filtering-policy-for-notifications
|
||||
- `GET /api/v1/notifications/requests`: https://docs.joinmastodon.org/methods/notifications/#get-requests
|
||||
|
@ -595,7 +594,6 @@ The following changelog entries focus on changes visible to users, administrator
|
|||
- `GET /api/v1/notifications/requests/merged`: https://docs.joinmastodon.org/methods/notifications/#requests-merged
|
||||
|
||||
In addition, accepting one or more notification requests generates a new streaming event:
|
||||
|
||||
- `notifications_merged`: an event of this type indicates accepted notification requests have finished merging, and the notifications list should be refreshed
|
||||
|
||||
- **Add notifications of severed relationships** (#27511, #29665, #29668, #29670, #29700, #29714, #29712, and #29731 by @ClearlyClaire and @Gargron)\
|
||||
|
|
8
Gemfile
8
Gemfile
|
@ -82,7 +82,7 @@ gem 'rqrcode', '~> 3.0'
|
|||
gem 'ruby-progressbar', '~> 1.13'
|
||||
gem 'sanitize', '~> 7.0'
|
||||
gem 'scenic', '~> 1.7'
|
||||
gem 'sidekiq', '< 8'
|
||||
gem 'sidekiq', '< 9'
|
||||
gem 'sidekiq-bulk', '~> 0.2.0'
|
||||
gem 'sidekiq-scheduler', '~> 6.0'
|
||||
gem 'sidekiq-unique-jobs', '> 8'
|
||||
|
@ -109,10 +109,10 @@ group :opentelemetry do
|
|||
gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.27.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.24.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.25.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.24.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
|
||||
|
|
138
Gemfile.lock
138
Gemfile.lock
|
@ -10,29 +10,29 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actioncable (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
activejob (= 8.0.2)
|
||||
activerecord (= 8.0.2)
|
||||
activestorage (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actionmailbox (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
activejob (= 8.0.2.1)
|
||||
activerecord (= 8.0.2.1)
|
||||
activestorage (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
actionview (= 8.0.2)
|
||||
activejob (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actionmailer (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
actionview (= 8.0.2.1)
|
||||
activejob (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (8.0.2)
|
||||
actionview (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actionpack (8.0.2.1)
|
||||
actionview (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
|
@ -40,15 +40,15 @@ GEM
|
|||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
useragent (~> 0.16)
|
||||
actiontext (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
activerecord (= 8.0.2)
|
||||
activestorage (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actiontext (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
activerecord (= 8.0.2.1)
|
||||
activestorage (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
actionview (8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
|
@ -58,22 +58,22 @@ GEM
|
|||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
activejob (8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
activerecord (8.0.2)
|
||||
activemodel (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
activemodel (8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
activerecord (8.0.2.1)
|
||||
activemodel (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
activejob (= 8.0.2)
|
||||
activerecord (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
activestorage (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
activejob (= 8.0.2.1)
|
||||
activerecord (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
marcel (~> 1.0)
|
||||
activesupport (8.0.2)
|
||||
activesupport (8.0.2.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
|
@ -547,19 +547,19 @@ GEM
|
|||
opentelemetry-instrumentation-concurrent_ruby (0.22.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-excon (0.23.0)
|
||||
opentelemetry-instrumentation-excon (0.24.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-faraday (0.27.0)
|
||||
opentelemetry-instrumentation-faraday (0.28.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-http (0.25.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-http_client (0.23.0)
|
||||
opentelemetry-instrumentation-http_client (0.24.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-net_http (0.23.0)
|
||||
opentelemetry-instrumentation-net_http (0.23.1)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||
opentelemetry-instrumentation-pg (0.30.1)
|
||||
|
@ -667,20 +667,20 @@ GEM
|
|||
rack (>= 1.3)
|
||||
rackup (2.2.1)
|
||||
rack (>= 3)
|
||||
rails (8.0.2)
|
||||
actioncable (= 8.0.2)
|
||||
actionmailbox (= 8.0.2)
|
||||
actionmailer (= 8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
actiontext (= 8.0.2)
|
||||
actionview (= 8.0.2)
|
||||
activejob (= 8.0.2)
|
||||
activemodel (= 8.0.2)
|
||||
activerecord (= 8.0.2)
|
||||
activestorage (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
rails (8.0.2.1)
|
||||
actioncable (= 8.0.2.1)
|
||||
actionmailbox (= 8.0.2.1)
|
||||
actionmailer (= 8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
actiontext (= 8.0.2.1)
|
||||
actionview (= 8.0.2.1)
|
||||
activejob (= 8.0.2.1)
|
||||
activemodel (= 8.0.2.1)
|
||||
activerecord (= 8.0.2.1)
|
||||
activestorage (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 8.0.2)
|
||||
railties (= 8.0.2.1)
|
||||
rails-dom-testing (2.3.0)
|
||||
activesupport (>= 5.0.0)
|
||||
minitest
|
||||
|
@ -691,9 +691,9 @@ GEM
|
|||
rails-i18n (8.0.1)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 8.0.0, < 9)
|
||||
railties (8.0.2)
|
||||
actionpack (= 8.0.2)
|
||||
activesupport (= 8.0.2)
|
||||
railties (8.0.2.1)
|
||||
actionpack (= 8.0.2.1)
|
||||
activesupport (= 8.0.2.1)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
@ -719,7 +719,7 @@ GEM
|
|||
redis (4.8.1)
|
||||
redis-client (0.25.2)
|
||||
connection_pool
|
||||
regexp_parser (2.11.1)
|
||||
regexp_parser (2.11.2)
|
||||
reline (0.6.2)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.7.0)
|
||||
|
@ -787,7 +787,7 @@ GEM
|
|||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.75.0, < 2.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
rubocop-rails (2.33.1)
|
||||
rubocop-rails (2.33.3)
|
||||
activesupport (>= 4.2.0)
|
||||
lint_roller (~> 1.1)
|
||||
rack (>= 1.1)
|
||||
|
@ -823,12 +823,12 @@ GEM
|
|||
securerandom (0.4.1)
|
||||
shoulda-matchers (6.5.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (7.3.9)
|
||||
base64
|
||||
connection_pool (>= 2.3.0)
|
||||
logger
|
||||
rack (>= 2.2.4)
|
||||
redis-client (>= 0.22.2)
|
||||
sidekiq (8.0.7)
|
||||
connection_pool (>= 2.5.0)
|
||||
json (>= 2.9.0)
|
||||
logger (>= 1.6.2)
|
||||
rack (>= 3.1.0)
|
||||
redis-client (>= 0.23.2)
|
||||
sidekiq-bulk (0.2.0)
|
||||
sidekiq
|
||||
sidekiq-scheduler (6.0.1)
|
||||
|
@ -1030,10 +1030,10 @@ DEPENDENCIES
|
|||
opentelemetry-instrumentation-active_job (~> 0.8.0)
|
||||
opentelemetry-instrumentation-active_model_serializers (~> 0.22.0)
|
||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
|
||||
opentelemetry-instrumentation-excon (~> 0.23.0)
|
||||
opentelemetry-instrumentation-faraday (~> 0.27.0)
|
||||
opentelemetry-instrumentation-excon (~> 0.24.0)
|
||||
opentelemetry-instrumentation-faraday (~> 0.28.0)
|
||||
opentelemetry-instrumentation-http (~> 0.25.0)
|
||||
opentelemetry-instrumentation-http_client (~> 0.23.0)
|
||||
opentelemetry-instrumentation-http_client (~> 0.24.0)
|
||||
opentelemetry-instrumentation-net_http (~> 0.23.0)
|
||||
opentelemetry-instrumentation-pg (~> 0.30.0)
|
||||
opentelemetry-instrumentation-rack (~> 0.26.0)
|
||||
|
@ -1077,7 +1077,7 @@ DEPENDENCIES
|
|||
sanitize (~> 7.0)
|
||||
scenic (~> 1.7)
|
||||
shoulda-matchers
|
||||
sidekiq (< 8)
|
||||
sidekiq (< 9)
|
||||
sidekiq-bulk (~> 0.2.0)
|
||||
sidekiq-scheduler (~> 6.0)
|
||||
sidekiq-unique-jobs (> 8)
|
||||
|
|
|
@ -58,7 +58,7 @@ Mastodon is a **free, open-source social network server** based on [ActivityPub]
|
|||
|
||||
- **Ruby** 3.2+
|
||||
- **PostgreSQL** 13+
|
||||
- **Redis** 6.2+
|
||||
- **Redis** 7.0+
|
||||
- **Node.js** 20+
|
||||
|
||||
This repository includes deployment configurations for **Docker and docker-compose**, as well as for other environments like Heroku and Scalingo. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). A [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the main documentation.
|
||||
|
|
3
Vagrantfile
vendored
3
Vagrantfile
vendored
|
@ -54,6 +54,7 @@ sudo apt-get install \
|
|||
pkg-config \
|
||||
protobuf-compiler \
|
||||
zlib1g-dev \
|
||||
libvips42t64 \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
|
@ -134,7 +135,7 @@ VAGRANTFILE_API_VERSION = "2"
|
|||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.box = "ubuntu/focal64"
|
||||
config.vm.box = "bento/ubuntu-24.04"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::InteractionPoliciesController < Api::V1::Statuses::BaseController
|
||||
include Api::InteractionPoliciesConcern
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
||||
before_action -> { check_feature_enabled }
|
||||
|
||||
def update
|
||||
authorize @status, :update?
|
||||
|
||||
@status.update!(quote_approval_policy: quote_approval_policy)
|
||||
|
||||
broadcast_updates! if @status.quote_approval_policy_previously_changed?
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_params
|
||||
params.permit(:quote_approval_policy)
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.outgoing_quotes_enabled?
|
||||
end
|
||||
|
||||
def broadcast_updates!
|
||||
DistributionWorker.perform_async(@status.id, { 'update' => true })
|
||||
ActivityPub::StatusUpdateDistributionWorker.perform_async(@status.id)
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
class Api::V1::StatusesController < Api::BaseController
|
||||
include Authorization
|
||||
include AsyncRefreshesConcern
|
||||
include Api::InteractionPoliciesConcern
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :update, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :update, :destroy]
|
||||
|
@ -205,23 +206,6 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
)
|
||||
end
|
||||
|
||||
def quote_approval_policy
|
||||
# TODO: handle `nil` separately
|
||||
return nil unless Mastodon::Feature.outgoing_quotes_enabled? && status_params[:quote_approval_policy].present?
|
||||
|
||||
case status_params[:quote_approval_policy]
|
||||
when 'public'
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
|
||||
when 'followers'
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
|
||||
when 'nobody'
|
||||
0
|
||||
else
|
||||
# TODO: raise more useful message
|
||||
raise ActiveRecord::RecordInvalid
|
||||
end
|
||||
end
|
||||
|
||||
def serializer_for_status
|
||||
@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
|
||||
end
|
||||
|
|
22
app/controllers/concerns/api/interaction_policies_concern.rb
Normal file
22
app/controllers/concerns/api/interaction_policies_concern.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api::InteractionPoliciesConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def quote_approval_policy
|
||||
# TODO: handle `nil` separately
|
||||
return nil unless Mastodon::Feature.outgoing_quotes_enabled? && status_params[:quote_approval_policy].present?
|
||||
|
||||
case status_params[:quote_approval_policy]
|
||||
when 'public'
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
|
||||
when 'followers'
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
|
||||
when 'nobody'
|
||||
0
|
||||
else
|
||||
# TODO: raise more useful message
|
||||
raise ActiveRecord::RecordInvalid
|
||||
end
|
||||
end
|
||||
end
|
|
@ -183,7 +183,7 @@ export function directCompose(account) {
|
|||
};
|
||||
}
|
||||
|
||||
export function submitCompose() {
|
||||
export function submitCompose(successCallback) {
|
||||
return function (dispatch, getState) {
|
||||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
|
@ -241,6 +241,9 @@ export function submitCompose() {
|
|||
|
||||
dispatch(insertIntoTagHistory(response.data.tags, status));
|
||||
dispatch(submitComposeSuccess({ ...response.data }));
|
||||
if (typeof successCallback === 'function') {
|
||||
successCallback(response.data);
|
||||
}
|
||||
|
||||
// To make the app more responsive, immediately push the status
|
||||
// into the columns
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { apiGetContext } from 'mastodon/api/statuses';
|
||||
import { apiGetContext, apiSetQuotePolicy } from 'mastodon/api/statuses';
|
||||
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
|
||||
|
||||
import type { ApiQuotePolicy } from '../api_types/quotes';
|
||||
|
||||
import { importFetchedStatuses } from './importer';
|
||||
|
||||
export const fetchContext = createDataLoadingThunk(
|
||||
|
@ -23,3 +25,10 @@ export const fetchContext = createDataLoadingThunk(
|
|||
export const completeContextRefresh = createAction<{ statusId: string }>(
|
||||
'status/context/complete',
|
||||
);
|
||||
|
||||
export const setStatusQuotePolicy = createDataLoadingThunk(
|
||||
'status/setQuotePolicy',
|
||||
({ statusId, policy }: { statusId: string; policy: ApiQuotePolicy }) => {
|
||||
return apiSetQuotePolicy(statusId, policy);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import api, { getAsyncRefreshHeader } from 'mastodon/api';
|
||||
import type { ApiContextJSON } from 'mastodon/api_types/statuses';
|
||||
import api, { apiRequestPut, getAsyncRefreshHeader } from 'mastodon/api';
|
||||
import type {
|
||||
ApiContextJSON,
|
||||
ApiStatusJSON,
|
||||
} from 'mastodon/api_types/statuses';
|
||||
|
||||
import type { ApiQuotePolicy } from '../api_types/quotes';
|
||||
|
||||
export const apiGetContext = async (statusId: string) => {
|
||||
const response = await api().request<ApiContextJSON>({
|
||||
|
@ -12,3 +17,15 @@ export const apiGetContext = async (statusId: string) => {
|
|||
refresh: getAsyncRefreshHeader(response),
|
||||
};
|
||||
};
|
||||
|
||||
export const apiSetQuotePolicy = async (
|
||||
statusId: string,
|
||||
policy: ApiQuotePolicy,
|
||||
) => {
|
||||
return apiRequestPut<ApiStatusJSON>(
|
||||
`v1/statuses/${statusId}/interaction_policy`,
|
||||
{
|
||||
quote_approval_policy: policy,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { ApiStatusJSON } from './statuses';
|
||||
|
||||
export type ApiQuoteState = 'accepted' | 'pending' | 'revoked' | 'unauthorized';
|
||||
export type ApiQuotePolicy = 'public' | 'followers' | 'nobody';
|
||||
export type ApiQuotePolicy = 'public' | 'followers' | 'nobody' | 'unknown';
|
||||
|
||||
interface ApiQuoteEmptyJSON {
|
||||
state: Exclude<ApiQuoteState, 'accepted'>;
|
||||
|
@ -21,3 +21,13 @@ interface ApiQuoteAcceptedJSON {
|
|||
}
|
||||
|
||||
export type ApiQuoteJSON = ApiQuoteAcceptedJSON | ApiQuoteEmptyJSON;
|
||||
|
||||
export interface ApiQuotePolicyJSON {
|
||||
automatic: ApiQuotePolicy[];
|
||||
manual: ApiQuotePolicy[];
|
||||
current_user: ApiQuotePolicy;
|
||||
}
|
||||
|
||||
export function isQuotePolicy(policy: string): policy is ApiQuotePolicy {
|
||||
return ['public', 'followers', 'nobody'].includes(policy);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { ApiAccountJSON } from './accounts';
|
|||
import type { ApiCustomEmojiJSON } from './custom_emoji';
|
||||
import type { ApiMediaAttachmentJSON } from './media_attachments';
|
||||
import type { ApiPollJSON } from './polls';
|
||||
import type { ApiQuoteJSON } from './quotes';
|
||||
import type { ApiQuoteJSON, ApiQuotePolicyJSON } from './quotes';
|
||||
|
||||
// See app/modals/status.rb
|
||||
export type StatusVisibility =
|
||||
|
@ -120,9 +120,16 @@ export interface ApiStatusJSON {
|
|||
card?: ApiPreviewCardJSON;
|
||||
poll?: ApiPollJSON;
|
||||
quote?: ApiQuoteJSON;
|
||||
quote_approval?: ApiQuotePolicyJSON;
|
||||
}
|
||||
|
||||
export interface ApiContextJSON {
|
||||
ancestors: ApiStatusJSON[];
|
||||
descendants: ApiStatusJSON[];
|
||||
}
|
||||
|
||||
export interface ApiStatusSourceJSON {
|
||||
id: string;
|
||||
text: string;
|
||||
spoiler_text: string;
|
||||
}
|
||||
|
|
114
app/javascript/mastodon/components/dropdown/index.tsx
Normal file
114
app/javascript/mastodon/components/dropdown/index.tsx
Normal file
|
@ -0,0 +1,114 @@
|
|||
import { useCallback, useId, useMemo, useRef, useState } from 'react';
|
||||
import type { ComponentPropsWithoutRef, FC } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import type { SelectItem } from '../dropdown_selector';
|
||||
import { DropdownSelector } from '../dropdown_selector';
|
||||
|
||||
interface DropdownProps {
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
items: SelectItem[];
|
||||
onChange: (value: string) => void;
|
||||
current: string;
|
||||
emptyText?: MessageDescriptor;
|
||||
classPrefix: string;
|
||||
}
|
||||
|
||||
export const Dropdown: FC<
|
||||
DropdownProps & Omit<ComponentPropsWithoutRef<'button'>, keyof DropdownProps>
|
||||
> = ({
|
||||
title,
|
||||
disabled,
|
||||
items,
|
||||
current,
|
||||
onChange,
|
||||
classPrefix,
|
||||
className,
|
||||
...buttonProps
|
||||
}) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const accessibilityId = useId();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleToggle = useCallback(() => {
|
||||
if (!disabled) {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
}
|
||||
}, [disabled]);
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, []);
|
||||
const currentText = useMemo(
|
||||
() => items.find((i) => i.value === current)?.text,
|
||||
[current, items],
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type='button'
|
||||
{...buttonProps}
|
||||
title={title}
|
||||
aria-expanded={open}
|
||||
aria-controls={accessibilityId}
|
||||
onClick={handleToggle}
|
||||
disabled={disabled}
|
||||
className={classNames(
|
||||
`${classPrefix}__button`,
|
||||
{
|
||||
active: open,
|
||||
disabled,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
ref={buttonRef}
|
||||
>
|
||||
{currentText ?? (
|
||||
<FormattedMessage
|
||||
id='dropdown.empty'
|
||||
defaultMessage='Select an option'
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<Overlay
|
||||
show={open}
|
||||
offset={[0, 4]}
|
||||
placement='bottom-start'
|
||||
onHide={handleClose}
|
||||
flip
|
||||
target={buttonRef.current}
|
||||
popperConfig={{
|
||||
strategy: 'fixed',
|
||||
}}
|
||||
>
|
||||
{({ props, placement }) => (
|
||||
<div {...props} className={`${classPrefix}__overlay`}>
|
||||
<div
|
||||
className={classNames(
|
||||
'dropdown-animation',
|
||||
`${classPrefix}__dropdown`,
|
||||
placement,
|
||||
)}
|
||||
id={accessibilityId}
|
||||
>
|
||||
<DropdownSelector
|
||||
items={items}
|
||||
value={current}
|
||||
onClose={handleClose}
|
||||
onChange={onChange}
|
||||
classNamePrefix={classPrefix}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -13,8 +13,8 @@ const listenerOptions = supportsPassiveEvents
|
|||
? { passive: true, capture: true }
|
||||
: true;
|
||||
|
||||
export interface SelectItem {
|
||||
value: string;
|
||||
export interface SelectItem<Value extends string = string> {
|
||||
value: Value;
|
||||
icon?: string;
|
||||
iconComponent?: IconProp;
|
||||
text: string;
|
||||
|
@ -24,7 +24,7 @@ export interface SelectItem {
|
|||
|
||||
interface Props {
|
||||
value: string;
|
||||
classNamePrefix: string;
|
||||
classNamePrefix?: string;
|
||||
style?: React.CSSProperties;
|
||||
items: SelectItem[];
|
||||
onChange: (value: string) => void;
|
||||
|
|
|
@ -29,6 +29,7 @@ import { Dropdown } from 'mastodon/components/dropdown_menu';
|
|||
import { me } from '../initial_state';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { isFeatureEnabled } from '../utils/environment';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -68,6 +69,7 @@ const messages = defineMessages({
|
|||
filter: { id: 'status.filter', defaultMessage: 'Filter this post' },
|
||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||
revokeQuote: { id: 'status.revoke_quote', defaultMessage: 'Remove my post from @{name}’s post' },
|
||||
quotePolicyChange: { id: 'status.quote_policy_change', defaultMessage: 'Change who can quote' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { status }) => {
|
||||
|
@ -89,6 +91,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
onReblog: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onRevokeQuote: PropTypes.func,
|
||||
onQuotePolicyChange: PropTypes.func,
|
||||
onDirect: PropTypes.func,
|
||||
onMention: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
|
@ -200,7 +203,11 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
handleRevokeQuoteClick = () => {
|
||||
this.props.onRevokeQuote(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleQuotePolicyChange = () => {
|
||||
this.props.onQuotePolicyChange(this.props.status);
|
||||
};
|
||||
|
||||
handleBlockClick = () => {
|
||||
const { status, relationship, onBlock, onUnblock } = this.props;
|
||||
|
@ -291,6 +298,9 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
if (writtenByMe || withDismiss) {
|
||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||
if (writtenByMe && isFeatureEnabled('outgoing_quotes') && !['private', 'direct'].includes(status.get('visibility'))) {
|
||||
menu.push({ text: intl.formatMessage(messages.quotePolicyChange), action: this.handleQuotePolicyChange });
|
||||
}
|
||||
menu.push(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,10 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||
dispatch(openModal({ modalType: 'CONFIRM_REVOKE_QUOTE', modalProps: { statusId: status.get('id'), quotedStatusId: status.getIn(['quote', 'quoted_status']) }}));
|
||||
},
|
||||
|
||||
onQuotePolicyChange(status) {
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId: status.get('id') } }));
|
||||
},
|
||||
|
||||
onEdit (status) {
|
||||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
|
|
|
@ -73,6 +73,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
maxChars: PropTypes.number,
|
||||
redirectOnSuccess: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -255,62 +256,60 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<Warning />
|
||||
|
||||
<div className={classNames('compose-form__highlightable', { active: highlighted })} ref={this.setRef}>
|
||||
<div className='compose-form__scrollable'>
|
||||
<EditIndicator />
|
||||
<EditIndicator />
|
||||
|
||||
{this.props.spoiler && (
|
||||
<div className='spoiler-input'>
|
||||
<div className='spoiler-input__border' />
|
||||
{this.props.spoiler && (
|
||||
<div className='spoiler-input'>
|
||||
<div className='spoiler-input__border' />
|
||||
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
disabled={isSubmitting}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
onKeyDown={this.handleKeyDownSpoiler}
|
||||
ref={this.setSpoilerText}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSpoilerSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
spellCheck
|
||||
/>
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
disabled={isSubmitting}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
onKeyDown={this.handleKeyDownSpoiler}
|
||||
ref={this.setSpoilerText}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSpoilerSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
spellCheck
|
||||
/>
|
||||
|
||||
<div className='spoiler-input__border' />
|
||||
</div>
|
||||
)}
|
||||
<div className='spoiler-input__border' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={this.textareaRef}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={isSubmitting}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onKeyDown={this.handleKeyDownPost}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
/>
|
||||
<div className='compose-form__dropdowns'>
|
||||
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
||||
<LanguageDropdown />
|
||||
</div>
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={this.textareaRef}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={isSubmitting}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onKeyDown={this.handleKeyDownPost}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
/>
|
||||
|
||||
<UploadForm />
|
||||
<PollForm />
|
||||
|
||||
<div className='compose-form__footer'>
|
||||
<div className='compose-form__dropdowns'>
|
||||
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
||||
<LanguageDropdown />
|
||||
</div>
|
||||
|
||||
<div className='compose-form__actions'>
|
||||
<div className='compose-form__buttons'>
|
||||
<UploadButtonContainer />
|
||||
|
|
|
@ -396,7 +396,7 @@ export const LanguageDropdown: React.FC = () => {
|
|||
warning: guess !== '' && guess !== value,
|
||||
})}
|
||||
>
|
||||
<Icon id='' icon={TranslateIcon} />
|
||||
<Icon id='translate' icon={TranslateIcon} />
|
||||
<span className='dropdown-button__label'>{current[2] ?? value}</span>
|
||||
</button>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
|
|||
import { DropdownSelector } from 'mastodon/components/dropdown_selector';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
export const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' },
|
||||
|
|
|
@ -34,7 +34,7 @@ const mapStateToProps = state => ({
|
|||
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
const mapDispatchToProps = (dispatch, props) => ({
|
||||
|
||||
onChange (text) {
|
||||
dispatch(changeCompose(text));
|
||||
|
@ -47,7 +47,11 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
modalProps: {},
|
||||
}));
|
||||
} else {
|
||||
dispatch(submitCompose());
|
||||
dispatch(submitCompose((status) => {
|
||||
if (props.redirectOnSuccess) {
|
||||
window.location.assign(status.url);
|
||||
}
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import ModalContainer from 'mastodon/features/ui/containers/modal_container';
|
|||
|
||||
const Compose = () => (
|
||||
<>
|
||||
<ComposeFormContainer autoFocus withoutNavigation />
|
||||
<ComposeFormContainer autoFocus withoutNavigation redirectOnSuccess />
|
||||
<AlertsController />
|
||||
<ModalContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
|
|
|
@ -26,6 +26,7 @@ import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/
|
|||
import { IconButton } from '../../../components/icon_button';
|
||||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||
import { me } from '../../../initial_state';
|
||||
import { isFeatureEnabled } from '@/mastodon/utils/environment';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -62,6 +63,7 @@ const messages = defineMessages({
|
|||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||
revokeQuote: { id: 'status.revoke_quote', defaultMessage: 'Remove my post from @{name}’s post' },
|
||||
quotePolicyChange: { id: 'status.quote_policy_change', defaultMessage: 'Change who can quote' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { status }) => {
|
||||
|
@ -84,6 +86,7 @@ class ActionBar extends PureComponent {
|
|||
onBookmark: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onRevokeQuote: PropTypes.func,
|
||||
onQuotePolicyChange: PropTypes.func,
|
||||
onEdit: PropTypes.func.isRequired,
|
||||
onDirect: PropTypes.func.isRequired,
|
||||
onMention: PropTypes.func.isRequired,
|
||||
|
@ -122,7 +125,11 @@ class ActionBar extends PureComponent {
|
|||
|
||||
handleRevokeQuoteClick = () => {
|
||||
this.props.onRevokeQuote(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleQuotePolicyChange = () => {
|
||||
this.props.onQuotePolicyChange(this.props.status);
|
||||
};
|
||||
|
||||
handleRedraftClick = () => {
|
||||
this.props.onDelete(this.props.status, true);
|
||||
|
@ -240,6 +247,9 @@ class ActionBar extends PureComponent {
|
|||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||
if (isFeatureEnabled('outgoing_quotes') && !['private', 'direct'].includes(status.get('visibility'))) {
|
||||
menu.push({ text: intl.formatMessage(messages.quotePolicyChange), action: this.handleQuotePolicyChange });
|
||||
}
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
||||
|
|
|
@ -265,6 +265,11 @@ class Status extends ImmutablePureComponent {
|
|||
dispatch(openModal({ modalType: 'CONFIRM_REVOKE_QUOTE', modalProps: { statusId: status.get('id'), quotedStatusId: status.getIn(['quote', 'quoted_status']) }}));
|
||||
};
|
||||
|
||||
handleQuotePolicyChange = (status) => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId: status.get('id') } }));
|
||||
};
|
||||
|
||||
handleEditClick = (status) => {
|
||||
const { dispatch, askReplyConfirmation } = this.props;
|
||||
|
||||
|
@ -642,6 +647,7 @@ class Status extends ImmutablePureComponent {
|
|||
onBookmark={this.handleBookmarkClick}
|
||||
onDelete={this.handleDeleteClick}
|
||||
onRevokeQuote={this.handleRevokeQuoteClick}
|
||||
onQuotePolicyChange={this.handleQuotePolicyChange}
|
||||
onEdit={this.handleEditClick}
|
||||
onDirect={this.handleDirectClick}
|
||||
onMention={this.handleMentionClick}
|
||||
|
|
|
@ -43,6 +43,7 @@ import { ImageModal } from './image_modal';
|
|||
import MediaModal from './media_modal';
|
||||
import { ModalPlaceholder } from './modal_placeholder';
|
||||
import VideoModal from './video_modal';
|
||||
import { VisibilityModal } from './visibility_modal';
|
||||
|
||||
export const MODAL_COMPONENTS = {
|
||||
'MEDIA': () => Promise.resolve({ default: MediaModal }),
|
||||
|
@ -76,6 +77,7 @@ export const MODAL_COMPONENTS = {
|
|||
'CLOSED_REGISTRATIONS': ClosedRegistrationsModal,
|
||||
'IGNORE_NOTIFICATIONS': IgnoreNotificationsModal,
|
||||
'ANNUAL_REPORT': AnnualReportModal,
|
||||
'COMPOSE_PRIVACY': () => Promise.resolve({ default: VisibilityModal }),
|
||||
};
|
||||
|
||||
export default class ModalRoot extends PureComponent {
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
import { forwardRef, useCallback, useId, useMemo } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { changeComposeVisibility } from '@/mastodon/actions/compose';
|
||||
import { setStatusQuotePolicy } from '@/mastodon/actions/statuses_typed';
|
||||
import type { ApiQuotePolicy } from '@/mastodon/api_types/quotes';
|
||||
import { isQuotePolicy } from '@/mastodon/api_types/quotes';
|
||||
import type { StatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import { Dropdown } from '@/mastodon/components/dropdown';
|
||||
import type { SelectItem } from '@/mastodon/components/dropdown_selector';
|
||||
import { IconButton } from '@/mastodon/components/icon_button';
|
||||
import { messages as privacyMessages } from '@/mastodon/features/compose/components/privacy_dropdown';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modals/confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
buttonTitle: {
|
||||
id: 'visibility_modal.button_title',
|
||||
defaultMessage: 'Set visibility',
|
||||
},
|
||||
quotePublic: {
|
||||
id: 'visibility_modal.quote_public',
|
||||
defaultMessage: 'Anyone',
|
||||
},
|
||||
quoteFollowers: {
|
||||
id: 'visibility_modal.quote_followers',
|
||||
defaultMessage: 'Followers only',
|
||||
},
|
||||
quoteNobody: {
|
||||
id: 'visibility_modal.quote_nobody',
|
||||
defaultMessage: 'No one',
|
||||
},
|
||||
});
|
||||
|
||||
interface VisibilityModalProps extends BaseConfirmationModalProps {
|
||||
statusId: string;
|
||||
}
|
||||
|
||||
const selectStatusPolicy = createAppSelector(
|
||||
[(state) => state.statuses, (_state, statusId: string) => statusId],
|
||||
(statuses, statusId) => {
|
||||
const status = statuses.get(statusId);
|
||||
if (!status) {
|
||||
return 'public';
|
||||
}
|
||||
const policy =
|
||||
(status.getIn(['quote_approval', 'automatic', 0]) as string) || 'nobody';
|
||||
const visibility = status.get('visibility') as StatusVisibility;
|
||||
|
||||
// If the status is private or direct, it cannot be quoted by anyone.
|
||||
if (visibility === 'private' || visibility === 'direct') {
|
||||
return 'nobody';
|
||||
}
|
||||
|
||||
// If the status has a specific quote policy, return it.
|
||||
if (isQuotePolicy(policy)) {
|
||||
return policy;
|
||||
}
|
||||
|
||||
// Otherwise, return the default based on visibility.
|
||||
if (visibility === 'unlisted') {
|
||||
return 'followers';
|
||||
}
|
||||
return 'public';
|
||||
},
|
||||
);
|
||||
|
||||
export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
({ onClose, statusId }, ref) => {
|
||||
const intl = useIntl();
|
||||
const currentVisibility = useAppSelector(
|
||||
(state) =>
|
||||
(state.statuses.getIn([statusId, 'visibility'], 'public') as
|
||||
| StatusVisibility
|
||||
| undefined) ?? 'public',
|
||||
);
|
||||
const currentQuotePolicy = useAppSelector((state) =>
|
||||
selectStatusPolicy(state, statusId),
|
||||
);
|
||||
const disableQuotePolicy =
|
||||
currentVisibility === 'private' || currentVisibility === 'direct';
|
||||
const isSaving = useAppSelector(
|
||||
(state) =>
|
||||
state.statuses.getIn([statusId, 'isSavingQuotePolicy']) === true,
|
||||
);
|
||||
|
||||
const visibilityItems = useMemo<SelectItem<StatusVisibility>[]>(
|
||||
() => [
|
||||
{
|
||||
value: 'public',
|
||||
text: intl.formatMessage(privacyMessages.public_short),
|
||||
meta: intl.formatMessage(privacyMessages.public_long),
|
||||
},
|
||||
{
|
||||
value: 'unlisted',
|
||||
text: intl.formatMessage(privacyMessages.unlisted_short),
|
||||
meta: intl.formatMessage(privacyMessages.unlisted_long),
|
||||
},
|
||||
{
|
||||
value: 'private',
|
||||
text: intl.formatMessage(privacyMessages.private_short),
|
||||
meta: intl.formatMessage(privacyMessages.private_long),
|
||||
},
|
||||
{
|
||||
value: 'direct',
|
||||
text: intl.formatMessage(privacyMessages.direct_short),
|
||||
meta: intl.formatMessage(privacyMessages.direct_long),
|
||||
},
|
||||
],
|
||||
[intl],
|
||||
);
|
||||
const quoteItems = useMemo<SelectItem<ApiQuotePolicy>[]>(
|
||||
() => [
|
||||
{ value: 'public', text: intl.formatMessage(messages.quotePublic) },
|
||||
{
|
||||
value: 'followers',
|
||||
text: intl.formatMessage(messages.quoteFollowers),
|
||||
},
|
||||
{ value: 'nobody', text: intl.formatMessage(messages.quoteNobody) },
|
||||
],
|
||||
[intl],
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleVisibilityChange = useCallback(
|
||||
(value: string) => {
|
||||
// Published statuses cannot change visibility.
|
||||
if (statusId) {
|
||||
return;
|
||||
}
|
||||
dispatch(changeComposeVisibility(value));
|
||||
},
|
||||
[dispatch, statusId],
|
||||
);
|
||||
const handleQuotePolicyChange = useCallback(
|
||||
(value: string) => {
|
||||
if (isQuotePolicy(value) && !disableQuotePolicy) {
|
||||
void dispatch(setStatusQuotePolicy({ policy: value, statusId }));
|
||||
}
|
||||
},
|
||||
[disableQuotePolicy, dispatch, statusId],
|
||||
);
|
||||
|
||||
const privacyDropdownId = useId();
|
||||
const quoteDropdownId = useId();
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal dialog-modal visibility-modal'>
|
||||
<div className='dialog-modal__header'>
|
||||
<IconButton
|
||||
className='dialog-modal__header__close'
|
||||
title={intl.formatMessage(messages.close)}
|
||||
icon='times'
|
||||
iconComponent={CloseIcon}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.header'
|
||||
defaultMessage='Visibility and interaction'
|
||||
>
|
||||
{(chunks) => (
|
||||
<span className='dialog-modal__header__title'>{chunks}</span>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
</div>
|
||||
<div className='dialog-modal__content'>
|
||||
<div className='dialog-modal__content__description'>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.instructions'
|
||||
defaultMessage='Control who can interact with this post. Global settings can be found under <link>Preferences > Other</link>.'
|
||||
values={{
|
||||
link: (chunks) => (
|
||||
<a href='/settings/preferences/other'>{chunks}</a>
|
||||
),
|
||||
}}
|
||||
tagName='p'
|
||||
/>
|
||||
</div>
|
||||
<div className='dialog-modal__content__form'>
|
||||
<label
|
||||
htmlFor={privacyDropdownId}
|
||||
className={classNames('visibility-dropdown__label', {
|
||||
disabled: isSaving || !!statusId,
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.privacy_label'
|
||||
defaultMessage='Privacy'
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
items={visibilityItems}
|
||||
classPrefix='visibility-dropdown'
|
||||
current={currentVisibility}
|
||||
onChange={handleVisibilityChange}
|
||||
title={intl.formatMessage(privacyMessages.change_privacy)}
|
||||
disabled={isSaving || !!statusId}
|
||||
id={privacyDropdownId}
|
||||
/>
|
||||
{!!statusId && (
|
||||
<p className='visibility-dropdown__helper'>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.helper.privacy_editing'
|
||||
defaultMessage="Visibility can't be changed after a post is published."
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</label>
|
||||
|
||||
<label
|
||||
htmlFor={quoteDropdownId}
|
||||
className={classNames('visibility-dropdown__label', {
|
||||
disabled: disableQuotePolicy || isSaving,
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.quote_label'
|
||||
defaultMessage='Change who can quote'
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
items={quoteItems}
|
||||
onChange={handleQuotePolicyChange}
|
||||
classPrefix='visibility-dropdown'
|
||||
current={currentQuotePolicy}
|
||||
title={intl.formatMessage(messages.buttonTitle)}
|
||||
disabled={disableQuotePolicy || isSaving}
|
||||
id={quoteDropdownId}
|
||||
/>
|
||||
<QuotePolicyHelper
|
||||
policy={currentQuotePolicy}
|
||||
visibility={currentVisibility}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
VisibilityModal.displayName = 'VisibilityModal';
|
||||
|
||||
const QuotePolicyHelper: FC<{
|
||||
policy: ApiQuotePolicy;
|
||||
visibility: StatusVisibility;
|
||||
}> = ({ policy, visibility }) => {
|
||||
if (visibility === 'unlisted' && policy !== 'nobody') {
|
||||
return (
|
||||
<p className='visibility-dropdown__helper'>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.helper.unlisted_quoting'
|
||||
defaultMessage='When people quote you, their post will also be hidden from trending timelines.'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (visibility === 'private') {
|
||||
return (
|
||||
<p className='visibility-dropdown__helper'>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.helper.private_quoting'
|
||||
defaultMessage="Follower-only posts can't be quoted."
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (visibility === 'direct') {
|
||||
return (
|
||||
<p className='visibility-dropdown__helper'>
|
||||
<FormattedMessage
|
||||
id='visibility_modal.helper.direct_quoting'
|
||||
defaultMessage="Private mentions can't be quoted."
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
|
@ -510,7 +510,7 @@
|
|||
"lightbox.zoom_out": "Přizpůsobit velikost",
|
||||
"limited_account_hint.action": "Přesto profil zobrazit",
|
||||
"limited_account_hint.title": "Tento profil byl skryt moderátory {domain}.",
|
||||
"link_preview.author": "Podle {name}",
|
||||
"link_preview.author": "Od {name}",
|
||||
"link_preview.more_from_author": "Více od {name}",
|
||||
"link_preview.shares": "{count, plural, one {{counter} příspěvek} few {{counter} příspěvky} many {{counter} příspěvků} other {{counter} příspěvků}}",
|
||||
"lists.add_member": "Přidat",
|
||||
|
|
|
@ -292,6 +292,7 @@
|
|||
"domain_pill.your_handle": "Your handle:",
|
||||
"domain_pill.your_server": "Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.",
|
||||
"domain_pill.your_username": "Your unique identifier on this server. It’s possible to find users with the same username on different servers.",
|
||||
"dropdown.empty": "Select an option",
|
||||
"embed.instructions": "Embed this post on your website by copying the code below.",
|
||||
"embed.preview": "Here is what it will look like:",
|
||||
"emoji_button.activity": "Activity",
|
||||
|
@ -884,6 +885,7 @@
|
|||
"status.quote_error.pending_approval": "Post pending",
|
||||
"status.quote_error.pending_approval_popout.body": "Quotes shared across the Fediverse may take time to display, as different servers have different protocols.",
|
||||
"status.quote_error.pending_approval_popout.title": "Pending quote? Remain calm",
|
||||
"status.quote_policy_change": "Change who can quote",
|
||||
"status.quote_post_author": "Quoted a post by @{name}",
|
||||
"status.read_more": "Read more",
|
||||
"status.reblog": "Boost",
|
||||
|
@ -959,5 +961,17 @@
|
|||
"video.skip_forward": "Skip forward",
|
||||
"video.unmute": "Unmute",
|
||||
"video.volume_down": "Volume down",
|
||||
"video.volume_up": "Volume up"
|
||||
"video.volume_up": "Volume up",
|
||||
"visibility_modal.button_title": "Set visibility",
|
||||
"visibility_modal.header": "Visibility and interaction",
|
||||
"visibility_modal.helper.direct_quoting": "Private mentions can't be quoted.",
|
||||
"visibility_modal.helper.privacy_editing": "Published posts cannot change their visibility.",
|
||||
"visibility_modal.helper.private_quoting": "Follower-only posts can't be quoted.",
|
||||
"visibility_modal.helper.unlisted_quoting": "When people quote you, their post will also be hidden from trending timelines.",
|
||||
"visibility_modal.instructions": "Control who can interact with this post. Global settings can be found under <link>Preferences > Other</link>.",
|
||||
"visibility_modal.privacy_label": "Privacy",
|
||||
"visibility_modal.quote_followers": "Followers only",
|
||||
"visibility_modal.quote_label": "Change who can quote",
|
||||
"visibility_modal.quote_nobody": "No one",
|
||||
"visibility_modal.quote_public": "Anyone"
|
||||
}
|
||||
|
|
|
@ -245,6 +245,9 @@
|
|||
"confirmations.remove_from_followers.confirm": "Fjern fylgjar",
|
||||
"confirmations.remove_from_followers.message": "{name} vil ikkje fylgja deg meir. Vil du halda fram?",
|
||||
"confirmations.remove_from_followers.title": "Fjern fylgjar?",
|
||||
"confirmations.revoke_quote.confirm": "Fjern innlegget",
|
||||
"confirmations.revoke_quote.message": "Du kan ikkje angra denne handlinga.",
|
||||
"confirmations.revoke_quote.title": "Fjern innlegget?",
|
||||
"confirmations.unfollow.confirm": "Slutt å fylgja",
|
||||
"confirmations.unfollow.message": "Er du sikker på at du vil slutta å fylgja {name}?",
|
||||
"confirmations.unfollow.title": "Slutt å fylgja brukaren?",
|
||||
|
@ -498,6 +501,8 @@
|
|||
"keyboard_shortcuts.translate": "å omsetje eit innlegg",
|
||||
"keyboard_shortcuts.unfocus": "for å fokusere vekk skrive-/søkefeltet",
|
||||
"keyboard_shortcuts.up": "Flytt opp på lista",
|
||||
"learn_more_link.got_it": "Forstått",
|
||||
"learn_more_link.learn_more": "Lær meir",
|
||||
"lightbox.close": "Lukk",
|
||||
"lightbox.next": "Neste",
|
||||
"lightbox.previous": "Førre",
|
||||
|
@ -598,6 +603,7 @@
|
|||
"notification.label.mention": "Omtale",
|
||||
"notification.label.private_mention": "Privat omtale",
|
||||
"notification.label.private_reply": "Privat svar",
|
||||
"notification.label.quote": "{name} siterte innlegget ditt",
|
||||
"notification.label.reply": "Svar",
|
||||
"notification.mention": "Omtale",
|
||||
"notification.mentioned_you": "{name} nemnde deg",
|
||||
|
@ -655,6 +661,7 @@
|
|||
"notifications.column_settings.mention": "Omtaler:",
|
||||
"notifications.column_settings.poll": "Røysteresultat:",
|
||||
"notifications.column_settings.push": "Pushvarsel",
|
||||
"notifications.column_settings.quote": "Sitat:",
|
||||
"notifications.column_settings.reblog": "Framhevingar:",
|
||||
"notifications.column_settings.show": "Vis i kolonne",
|
||||
"notifications.column_settings.sound": "Spel av lyd",
|
||||
|
@ -845,6 +852,8 @@
|
|||
"status.bookmark": "Set bokmerke",
|
||||
"status.cancel_reblog_private": "Opphev framheving",
|
||||
"status.cannot_reblog": "Du kan ikkje framheva dette innlegget",
|
||||
"status.context.load_new_replies": "Nye svar finst",
|
||||
"status.context.loading": "Ser etter fleire svar",
|
||||
"status.continued_thread": "Framhald til tråden",
|
||||
"status.copy": "Kopier lenke til status",
|
||||
"status.delete": "Slett",
|
||||
|
@ -871,6 +880,11 @@
|
|||
"status.open": "Utvid denne statusen",
|
||||
"status.pin": "Fest på profil",
|
||||
"status.quote_error.filtered": "Gøymt på grunn av eitt av filtra dine",
|
||||
"status.quote_error.not_available": "Innlegget er ikkje tilgjengeleg",
|
||||
"status.quote_error.pending_approval": "Innlegget ventar",
|
||||
"status.quote_error.pending_approval_popout.body": "Sitat frå rundt om i allheimen kan ta tid å visa, fordi ulike tenarar har ulike protokollar.",
|
||||
"status.quote_error.pending_approval_popout.title": "Ventande sitat? Ikkje stress",
|
||||
"status.quote_post_author": "Siterte eit innlegg av @{name}",
|
||||
"status.read_more": "Les meir",
|
||||
"status.reblog": "Framhev",
|
||||
"status.reblog_private": "Framhev til dei originale mottakarane",
|
||||
|
@ -885,6 +899,7 @@
|
|||
"status.reply": "Svar",
|
||||
"status.replyAll": "Svar til tråd",
|
||||
"status.report": "Rapporter @{name}",
|
||||
"status.revoke_quote": "Fjern innlegget mitt frå @{name} sitt innlegg",
|
||||
"status.sensitive_warning": "Ømtolig innhald",
|
||||
"status.share": "Del",
|
||||
"status.show_less_all": "Vis mindre for alle",
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
STATUS_FETCH_REQUEST,
|
||||
STATUS_FETCH_FAIL,
|
||||
} from '../actions/statuses';
|
||||
import { setStatusQuotePolicy } from '../actions/statuses_typed';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
|
@ -70,6 +71,22 @@ const initialState = ImmutableMap();
|
|||
|
||||
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
|
||||
export default function statuses(state = initialState, action) {
|
||||
if (setStatusQuotePolicy.pending.match(action)) {
|
||||
const status = state.get(action.meta.arg.statusId);
|
||||
if (status) {
|
||||
return state.setIn([action.meta.arg.statusId, 'isSavingQuotePolicy'], true);
|
||||
}
|
||||
} else if (setStatusQuotePolicy.fulfilled.match(action)) {
|
||||
const status = state.get(action.payload.id);
|
||||
if (status) {
|
||||
return state
|
||||
.setIn([action.payload.id, 'quote_approval'], action.payload.quote_approval)
|
||||
.deleteIn([action.payload.id, 'isSavingQuotePolicy']);
|
||||
}
|
||||
} else if (setStatusQuotePolicy.rejected.match(action)) {
|
||||
return state.deleteIn([action.meta.arg.statusId, 'isSavingQuotePolicy']);
|
||||
}
|
||||
|
||||
switch(action.type) {
|
||||
case STATUS_FETCH_REQUEST:
|
||||
return state.setIn([action.id, 'isLoading'], true);
|
||||
|
|
|
@ -40,7 +40,10 @@ interface AppThunkConfig {
|
|||
fulfilledMeta: AppMeta;
|
||||
rejectedMeta: AppMeta;
|
||||
}
|
||||
type AppThunkApi = Pick<GetThunkAPI<AppThunkConfig>, 'getState' | 'dispatch'>;
|
||||
export type AppThunkApi = Pick<
|
||||
GetThunkAPI<AppThunkConfig>,
|
||||
'getState' | 'dispatch'
|
||||
>;
|
||||
|
||||
interface AppThunkOptions<Arg> {
|
||||
useLoadingBar?: boolean;
|
||||
|
|
|
@ -12,7 +12,11 @@ export function isProduction() {
|
|||
else return import.meta.env.PROD;
|
||||
}
|
||||
|
||||
export type Features = 'modern_emojis';
|
||||
export type Features =
|
||||
| 'modern_emojis'
|
||||
| 'outgoing_quotes'
|
||||
| 'fasp'
|
||||
| 'http_message_signatures';
|
||||
|
||||
export function isFeatureEnabled(feature: Features) {
|
||||
return initialState?.features.includes(feature) ?? false;
|
||||
|
|
|
@ -602,15 +602,12 @@ body > [data-popper-placement] {
|
|||
&__highlightable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
flex: 0 1 auto;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--background-border-color);
|
||||
transition: border-color 300ms linear;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
background: var(--input-background-color);
|
||||
overflow-y: auto;
|
||||
|
||||
&.active {
|
||||
transition: none;
|
||||
|
@ -705,6 +702,8 @@ body > [data-popper-placement] {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > div {
|
||||
overflow: hidden;
|
||||
|
@ -715,6 +714,7 @@ body > [data-popper-placement] {
|
|||
&__uploads {
|
||||
padding: 0 12px;
|
||||
aspect-ratio: 3/2;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.media-gallery {
|
||||
|
@ -813,7 +813,6 @@ body > [data-popper-placement] {
|
|||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&__submit {
|
||||
|
@ -874,6 +873,7 @@ body > [data-popper-placement] {
|
|||
}
|
||||
|
||||
&__poll {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
|
@ -3518,11 +3518,10 @@ a.account__display-name {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - 10px);
|
||||
overflow-y: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.compose-form {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5403,7 +5402,8 @@ a.status-card {
|
|||
}
|
||||
|
||||
.privacy-dropdown__dropdown,
|
||||
.language-dropdown__dropdown {
|
||||
.language-dropdown__dropdown,
|
||||
.visibility-dropdown__dropdown {
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
background: var(--dropdown-background-color);
|
||||
backdrop-filter: $backdrop-blur-filter;
|
||||
|
@ -5432,7 +5432,8 @@ a.status-card {
|
|||
z-index: 9999;
|
||||
}
|
||||
|
||||
.privacy-dropdown__option {
|
||||
.privacy-dropdown__option,
|
||||
.visibility-dropdown__option {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.25px;
|
||||
|
@ -5578,6 +5579,39 @@ a.status-card {
|
|||
}
|
||||
}
|
||||
|
||||
.visibility-dropdown {
|
||||
&__overlay[data-popper-placement] {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
&__label.disabled {
|
||||
cursor: default;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&__button {
|
||||
color: $primary-text-color;
|
||||
background: var(--dropdown-background-color);
|
||||
border: 1px solid var(--dropdown-border-color);
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
&__helper {
|
||||
margin-top: 4px;
|
||||
font-size: 0.8em;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
margin-bottom: 32px;
|
||||
position: relative;
|
||||
|
@ -5870,6 +5904,17 @@ a.status-card {
|
|||
}
|
||||
}
|
||||
|
||||
.modal-root label {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-modal .video-player {
|
||||
max-height: 80vh;
|
||||
max-width: 100vw;
|
||||
|
@ -6376,6 +6421,15 @@ a.status-card {
|
|||
letter-spacing: 0.25px;
|
||||
overflow-y: auto;
|
||||
|
||||
&__description {
|
||||
margin: 24px 24px 0;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
:root {
|
||||
--dropdown-border-color: #{lighten($ui-base-color, 4%)};
|
||||
--dropdown-background-color: #{rgba(darken($ui-base-color, 8%), 0.9)};
|
||||
--dropdown-shadow: 0 20px 25px -5px #{rgba($base-shadow-color, 0.25)},
|
||||
--dropdown-shadow:
|
||||
0 20px 25px -5px #{rgba($base-shadow-color, 0.25)},
|
||||
0 8px 10px -6px #{rgba($base-shadow-color, 0.25)};
|
||||
--modal-background-color: #{rgba(darken($ui-base-color, 8%), 0.7)};
|
||||
--modal-background-variant-color: #{rgba($ui-base-color, 0.7)};
|
||||
|
|
|
@ -71,8 +71,16 @@ class Antispam
|
|||
end
|
||||
|
||||
def report_if_needed!(account)
|
||||
return if Report.unresolved.exists?(account: Account.representative, target_account: account)
|
||||
return if system_reports.unresolved.exists?(target_account: account)
|
||||
|
||||
Report.create!(account: Account.representative, target_account: account, category: :spam, comment: 'Account automatically reported for posting a banned URL')
|
||||
system_reports.create!(
|
||||
category: :spam,
|
||||
comment: 'Account automatically reported for posting a banned URL',
|
||||
target_account: account
|
||||
)
|
||||
end
|
||||
|
||||
def system_reports
|
||||
Account.representative.reports
|
||||
end
|
||||
end
|
||||
|
|
|
@ -116,7 +116,7 @@ class Account < ApplicationRecord
|
|||
|
||||
# Local user validations
|
||||
validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: USERNAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_username? && !actor_type_application? }
|
||||
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? && !actor_type_application? }
|
||||
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? && !actor_type_application? && !user&.bypass_registration_checks }
|
||||
validates :display_name, length: { maximum: DISPLAY_NAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_display_name? }
|
||||
validates :note, note_length: { maximum: NOTE_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_note? }
|
||||
validates :fields, length: { maximum: DEFAULT_FIELDS_SIZE }, if: -> { local? && will_save_change_to_fields? }
|
||||
|
|
|
@ -10,6 +10,10 @@ module Status::InteractionPolicyConcern
|
|||
followed: (1 << 3),
|
||||
}.freeze
|
||||
|
||||
included do
|
||||
before_validation :downgrade_quote_policy, if: -> { local? && !distributable? }
|
||||
end
|
||||
|
||||
def quote_policy_as_keys(kind)
|
||||
case kind
|
||||
when :automatic
|
||||
|
@ -52,4 +56,8 @@ module Status::InteractionPolicyConcern
|
|||
|
||||
:denied
|
||||
end
|
||||
|
||||
def downgrade_quote_policy
|
||||
self.quote_approval_policy = 0
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ module BulkMailingConcern
|
|||
job_class = ActionMailer::MailDeliveryJob
|
||||
|
||||
Sidekiq::Client.push_bulk({
|
||||
'class' => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
|
||||
'class' => Sidekiq::ActiveJob::Wrapper,
|
||||
'wrapped' => job_class,
|
||||
'queue' => mailer_class.deliver_later_queue_name,
|
||||
'args' => args_array.map do |args|
|
||||
|
|
|
@ -190,6 +190,7 @@ nn:
|
|||
create_relay: Opprett eit relé
|
||||
create_unavailable_domain: Opprett utilgjengeleg domene
|
||||
create_user_role: Opprett rolle
|
||||
create_username_block: Lag regel for brukarnamn
|
||||
demote_user: Degrader brukar
|
||||
destroy_announcement: Slett lysinga
|
||||
destroy_canonical_email_block: Fjern e-postblokkering
|
||||
|
@ -203,6 +204,7 @@ nn:
|
|||
destroy_status: Slett status
|
||||
destroy_unavailable_domain: Slett utilgjengeleg domene
|
||||
destroy_user_role: Øydelegg rolle
|
||||
destroy_username_block: Slett regel for brukarnamn
|
||||
disable_2fa_user: Skruv av 2FA
|
||||
disable_custom_emoji: Skruv av tilpassa emoji
|
||||
disable_relay: Skru av reléet
|
||||
|
@ -237,6 +239,7 @@ nn:
|
|||
update_report: Oppdater rapport
|
||||
update_status: Oppdater tut
|
||||
update_user_role: Oppdater rolla
|
||||
update_username_block: Oppdater regel for brukarnamn
|
||||
actions:
|
||||
approve_appeal_html: "%{name} godkjende klagen frå %{target} på modereringa"
|
||||
approve_user_html: "%{name} godkjende registreringa til %{target}"
|
||||
|
@ -255,6 +258,7 @@ nn:
|
|||
create_relay_html: "%{name} laga reléet %{target}"
|
||||
create_unavailable_domain_html: "%{name} stogga levering til domenet %{target}"
|
||||
create_user_role_html: "%{name} oppretta rolla %{target}"
|
||||
create_username_block_html: "%{name} laga ein regel for brukarnamn som inneheld %{target}"
|
||||
demote_user_html: "%{name} degraderte brukaren %{target}"
|
||||
destroy_announcement_html: "%{name} sletta kunngjeringa %{target}"
|
||||
destroy_canonical_email_block_html: "%{name} avblokkerte e-post med hash %{target}"
|
||||
|
@ -268,6 +272,7 @@ nn:
|
|||
destroy_status_html: "%{name} fjerna innlegget frå %{target}"
|
||||
destroy_unavailable_domain_html: "%{name} tok opp att levering til domenet %{target}"
|
||||
destroy_user_role_html: "%{name} sletta rolla %{target}"
|
||||
destroy_username_block_html: "%{name} fjerna regelen for brukarnamn som inneheld %{target}"
|
||||
disable_2fa_user_html: "%{name} tok vekk krav om tofaktorautentisering for brukaren %{target}"
|
||||
disable_custom_emoji_html: "%{name} deaktiverte emojien %{target}"
|
||||
disable_relay_html: "%{name} skrudde av reléet %{target}"
|
||||
|
@ -302,6 +307,7 @@ nn:
|
|||
update_report_html: "%{name} oppdaterte rapporten %{target}"
|
||||
update_status_html: "%{name} oppdaterte innlegg av %{target}"
|
||||
update_user_role_html: "%{name} endret %{target} -rolle"
|
||||
update_username_block_html: "%{name} oppdaterte regelen for brukarnamn som inneheld %{target}"
|
||||
deleted_account: sletta konto
|
||||
empty: Ingen loggar funne.
|
||||
filter_by_action: Sorter etter handling
|
||||
|
@ -1085,6 +1091,25 @@ nn:
|
|||
other: Brukt av %{count} personer i løpet av den seneste uken
|
||||
title: Anbefalingar og trendar
|
||||
trending: Trender
|
||||
username_blocks:
|
||||
add_new: Lag ny
|
||||
block_registrations: Blokker registreringar
|
||||
comparison:
|
||||
contains: Inneheld
|
||||
equals: Er lik
|
||||
contains_html: Inneheld %{string}
|
||||
created_msg: Laga regelen for brukarnamn
|
||||
delete: Slett
|
||||
edit:
|
||||
title: Rediger regelen for brukarnamn
|
||||
matches_exactly_html: Er lik %{string}
|
||||
new:
|
||||
create: Lag regel
|
||||
title: Lag ein ny regel for brukarnamn
|
||||
no_username_block_selected: Ingen brukarnamnreglar vart endra fordi du ikkje valde nokon
|
||||
not_permitted: Ikkje tillate
|
||||
title: Reglar for brukarnamn
|
||||
updated_msg: Oppdaterte regelen for brukarnamn
|
||||
warning_presets:
|
||||
add_new: Legg til ny
|
||||
delete: Slett
|
||||
|
@ -1349,6 +1374,10 @@ nn:
|
|||
basic_information: Grunnleggande informasjon
|
||||
hint_html: "<strong>Tilpass kva folk ser på den offentlege profilen din og ved sida av innlegga dine.</strong> Andre vil i større grad fylgja og samhandla med deg når du har eit profilbilete og har fyllt ut profilen din."
|
||||
other: Anna
|
||||
emoji_styles:
|
||||
auto: Auto
|
||||
native: Innebygd
|
||||
twemoji: Twemoji
|
||||
errors:
|
||||
'400': Søknaden du sende var ugyldig eller sett opp feil.
|
||||
'403': Du har ikkje løyve til å sjå denne sida.
|
||||
|
@ -1658,6 +1687,10 @@ nn:
|
|||
title: Ny omtale
|
||||
poll:
|
||||
subject: Meiningsmålinga frå %{name} er avslutta
|
||||
quote:
|
||||
body: 'Innlegget ditt vart sitert av %{name}:'
|
||||
subject: "%{name} siterte innlegget ditt"
|
||||
title: Nytt sitat
|
||||
reblog:
|
||||
body: 'Statusen din vart framheva av %{name}:'
|
||||
subject: "%{name} framheva statusen din"
|
||||
|
@ -1868,6 +1901,7 @@ nn:
|
|||
edited_at_html: Redigert %{date}
|
||||
errors:
|
||||
in_reply_not_found: Det ser ut til at tutet du freistar å svara ikkje finst.
|
||||
quoted_status_not_found: Innlegget du prøver å sitera ser ikkje ut til å finnast.
|
||||
over_character_limit: øvregrensa for teikn, %{max}, er nådd
|
||||
pin_errors:
|
||||
direct: Innlegg som bare er synlige for nevnte brukere kan ikke festes
|
||||
|
@ -1875,6 +1909,8 @@ nn:
|
|||
ownership: Du kan ikkje festa andre sine tut
|
||||
reblog: Ei framheving kan ikkje festast
|
||||
quote_policies:
|
||||
followers: Berre dei som fylgjer deg
|
||||
nobody: Ingen
|
||||
public: Alle
|
||||
title: "%{name}: «%{quote}»"
|
||||
visibilities:
|
||||
|
|
|
@ -56,10 +56,12 @@ nn:
|
|||
scopes: API-ane som programmet vil få tilgjenge til. Ettersom du vel eit toppnivåomfang tarv du ikkje velja einskilde API-ar.
|
||||
setting_aggregate_reblogs: Ikkje vis nye framhevingar for tut som nyleg har vorte heva fram (Påverkar berre nylege framhevingar)
|
||||
setting_always_send_emails: Vanlegvis vil ikkje e-postvarsel bli sendt når du brukar Mastodon aktivt
|
||||
setting_default_quote_policy: Denne innstillinga har berre verknad for innlegg som er laga med den neste utgåva av Mastodon, men du kan velja kva du vil ha i førebuingane.
|
||||
setting_default_sensitive: Sensitive media vert gøymde som standard, og du syner dei ved å klikka på dei
|
||||
setting_display_media_default: Gøym media som er merka som sensitive
|
||||
setting_display_media_hide_all: Alltid skjul alt media
|
||||
setting_display_media_show_all: Vis alltid media
|
||||
setting_emoji_style: Korleis du skal visa smilefjes. «Auto» prøver å visa innebygde smilefjes, men bruker Twemoji som reserveløysing for eldre nettlesarar.
|
||||
setting_system_scrollbars_ui: Gjeld berre skrivebordsnettlesarar som er bygde på Safari og Chrome
|
||||
setting_use_blurhash: Overgangar er basert på fargane til skjulte grafikkelement, men gjer detaljar utydelege
|
||||
setting_use_pending_items: Gøym tidslineoppdateringar bak eit klikk, i staden for å rulla ned automatisk
|
||||
|
@ -148,6 +150,9 @@ nn:
|
|||
min_age: Skal ikkje vere under minstealder som krevst av lover i jurisdiksjonen din.
|
||||
user:
|
||||
chosen_languages: Når merka vil berre tuta på dei valde språka synast på offentlege tidsliner
|
||||
date_of_birth:
|
||||
one: Me må sikra oss at du er minst %{count} for å bruka %{domain}. Me lagrar ikkje dette.
|
||||
other: Me må sikra oss at du er minst %{count} for å bruka %{domain}. Me lagrar ikkje dette.
|
||||
role: Rolla kontrollerer kva løyve brukaren har.
|
||||
user_role:
|
||||
color: Fargen som skal nyttast for denne rolla i heile brukargrensesnittet, som RGB i hex-format
|
||||
|
@ -155,6 +160,10 @@ nn:
|
|||
name: Offentleg namn på rolla, dersom rolla skal visast som eit emblem
|
||||
permissions_as_keys: Brukarar med denne rolla vil ha tilgang til...
|
||||
position: Høgare rolle avgjer konfliktløysing i visse situasjonar. Visse handlingar kan berre utførast på roller med lågare prioritet
|
||||
username_block:
|
||||
allow_with_approval: I staden for å hindra registreringar i det heile, må du godkjenna registreringar som passar
|
||||
comparison: Ver merksam på Scunthorpe-problemet når du blokkerer delvise treff
|
||||
username: Vil passa uansett store og små bokstavar og vanlege homoglyfar som «4» for «a» eller «3» for «e»
|
||||
webhook:
|
||||
events: Vel hendingar å senda
|
||||
template: Skriv di eiga JSON nyttelast ved å bruka variabel interpolering. La stå tom for standard JSON.
|
||||
|
@ -237,6 +246,7 @@ nn:
|
|||
setting_display_media_default: Standard
|
||||
setting_display_media_hide_all: Gøym alle
|
||||
setting_display_media_show_all: Vis alle
|
||||
setting_emoji_style: Stil for smilefjes
|
||||
setting_expand_spoilers: Vid alltid ut tut som er merka med innhaldsåtvaringar
|
||||
setting_hide_network: Gøym nettverket ditt
|
||||
setting_missing_alt_text_modal: Vis stadfestingsdialog før du legg ut media utan alt-tekst
|
||||
|
@ -319,6 +329,7 @@ nn:
|
|||
follow_request: Send e-post når nokon spør om å fylgja deg
|
||||
mention: Send e-post når nokon nemner deg
|
||||
pending_account: Send e-post når ein ny konto treng gjennomgang
|
||||
quote: Nokon siterte deg
|
||||
reblog: Send e-post når nokon framhevar statusen din
|
||||
report: Ny rapport er sendt
|
||||
software_updates:
|
||||
|
@ -365,6 +376,10 @@ nn:
|
|||
name: Namn
|
||||
permissions_as_keys: Løyve
|
||||
position: Prioritet
|
||||
username_block:
|
||||
allow_with_approval: Tillat registreringar med godkjenning
|
||||
comparison: Samanlikningsmetode
|
||||
username: Ord som skal passa
|
||||
webhook:
|
||||
events: Aktiverte hendingar
|
||||
template: Nyttelastmal
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace :api, format: false do
|
|||
resource :history, only: :show
|
||||
resource :source, only: :show
|
||||
|
||||
resource :interaction_policy, only: :update
|
||||
|
||||
post :translate, to: 'translations#create'
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"@gamestdio/websocket": "^0.3.2",
|
||||
"@github/webauthn-json": "^2.1.1",
|
||||
"@optimize-lodash/rollup-plugin": "^5.0.2",
|
||||
"@rails/ujs": "7.1.501",
|
||||
"@rails/ujs": "7.1.502",
|
||||
"@react-spring/web": "^9.7.5",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
|
@ -170,7 +170,7 @@
|
|||
"eslint-import-resolver-typescript": "^4.2.5",
|
||||
"eslint-plugin-formatjs": "^5.3.1",
|
||||
"eslint-plugin-import": "~2.32.0",
|
||||
"eslint-plugin-jsdoc": "^53.0.0",
|
||||
"eslint-plugin-jsdoc": "^54.0.0",
|
||||
"eslint-plugin-jsx-a11y": "~6.10.2",
|
||||
"eslint-plugin-promise": "~7.2.1",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
|
|
47
spec/lib/antispam_spec.rb
Normal file
47
spec/lib/antispam_spec.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Antispam do
|
||||
describe '#local_preflight_check!' do
|
||||
subject { described_class.new.local_preflight_check!(status) }
|
||||
|
||||
let(:status) { Fabricate :status }
|
||||
|
||||
context 'when there is no spammy text registered' do
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context 'with spammy text' do
|
||||
before { redis.sadd 'antispam:spammy_texts', 'https://banned.example' }
|
||||
|
||||
context 'when status matches' do
|
||||
let(:status) { Fabricate :status, text: 'I use https://banned.example urls in my text' }
|
||||
|
||||
it 'raises error and reports' do
|
||||
expect { subject }
|
||||
.to raise_error(described_class::SilentlyDrop)
|
||||
.and change(spam_reports, :count).by(1)
|
||||
end
|
||||
|
||||
context 'when report already exists' do
|
||||
before { Fabricate :report, account: Account.representative, target_account: status.account }
|
||||
|
||||
it 'raises error and does not report' do
|
||||
expect { subject }
|
||||
.to raise_error(described_class::SilentlyDrop)
|
||||
.and not_change(spam_reports, :count)
|
||||
end
|
||||
end
|
||||
|
||||
def spam_reports
|
||||
Account.representative.reports.where(target_account: status.account).spam
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status does not match' do
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -32,6 +32,7 @@ RSpec.describe Mastodon::CLI::Accounts do
|
|||
|
||||
describe '#create' do
|
||||
let(:action) { :create }
|
||||
let(:username) { 'tootctl_username' }
|
||||
|
||||
shared_examples 'a new user with given email address and username' do
|
||||
it 'creates user and accounts from options and displays success message' do
|
||||
|
@ -48,18 +49,24 @@ RSpec.describe Mastodon::CLI::Accounts do
|
|||
end
|
||||
|
||||
def account_from_options
|
||||
Account.find_local('tootctl_username')
|
||||
Account.find_local(username)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when required USERNAME and --email are provided' do
|
||||
let(:arguments) { ['tootctl_username'] }
|
||||
let(:arguments) { [username] }
|
||||
|
||||
context 'with USERNAME and --email only' do
|
||||
let(:options) { { email: 'tootctl@example.com' } }
|
||||
|
||||
it_behaves_like 'a new user with given email address and username'
|
||||
|
||||
context 'with a reserved username' do
|
||||
let(:username) { 'security' }
|
||||
|
||||
it_behaves_like 'a new user with given email address and username'
|
||||
end
|
||||
|
||||
context 'with invalid --email value' do
|
||||
let(:options) { { email: 'invalid' } }
|
||||
|
||||
|
|
120
spec/requests/api/v1/statuses/interaction_policies_spec.rb
Normal file
120
spec/requests/api/v1/statuses/interaction_policies_spec.rb
Normal file
|
@ -0,0 +1,120 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Interaction policies', feature: :outgoing_quotes do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'write:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
let(:params) { { quote_approval_policy: 'followers' } }
|
||||
|
||||
describe 'PUT /api/v1/statuses/:status_id/interaction_policy' do
|
||||
subject do
|
||||
put "/api/v1/statuses/#{status.id}/interaction_policy", headers: headers, params: params
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:statuses'
|
||||
|
||||
context 'without an authorization header' do
|
||||
let(:headers) { {} }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
expect { subject }
|
||||
.to_not(change { status.reload.quote_approval_policy })
|
||||
|
||||
expect(response).to have_http_status(401)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a status from a different user' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
expect { subject }
|
||||
.to_not(change { status.reload.quote_approval_policy })
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when changing the interaction policy' do
|
||||
it 'changes the interaction policy, returns the updated status, and schedules distribution jobs' do
|
||||
expect { subject }
|
||||
.to change { status.reload.quote_approval_policy }.to(Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body).to include(
|
||||
'quote_approval' => match(
|
||||
'automatic' => ['followers'],
|
||||
'manual' => [],
|
||||
'current_user' => 'automatic'
|
||||
)
|
||||
)
|
||||
|
||||
expect(DistributionWorker)
|
||||
.to have_enqueued_sidekiq_job(status.id, { 'update' => true })
|
||||
expect(ActivityPub::StatusUpdateDistributionWorker)
|
||||
.to have_enqueued_sidekiq_job(status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not changing the interaction policy' do
|
||||
let(:params) { { quote_approval_policy: 'nobody' } }
|
||||
|
||||
it 'keeps the interaction policy, returns the status, and does not schedule distribution jobs' do
|
||||
expect { subject }
|
||||
.to_not(change { status.reload.quote_approval_policy }.from(0))
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body).to include(
|
||||
'quote_approval' => match(
|
||||
'automatic' => [],
|
||||
'manual' => [],
|
||||
'current_user' => 'automatic'
|
||||
)
|
||||
)
|
||||
|
||||
expect(DistributionWorker)
|
||||
.to_not have_enqueued_sidekiq_job
|
||||
expect(ActivityPub::StatusUpdateDistributionWorker)
|
||||
.to_not have_enqueued_sidekiq_job
|
||||
end
|
||||
end
|
||||
|
||||
context 'when trying to change the interaction policy of a private post' do
|
||||
let(:status) { Fabricate(:status, account: user.account, visibility: :private) }
|
||||
let(:params) { { quote_approval_policy: 'public' } }
|
||||
|
||||
it 'keeps the interaction policy, returns the status, and does not schedule distribution jobs' do
|
||||
expect { subject }
|
||||
.to_not(change { status.reload.quote_approval_policy }.from(0))
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type)
|
||||
.to start_with('application/json')
|
||||
expect(response.parsed_body).to include(
|
||||
'quote_approval' => match(
|
||||
'automatic' => [],
|
||||
'manual' => [],
|
||||
'current_user' => 'automatic'
|
||||
)
|
||||
)
|
||||
|
||||
expect(DistributionWorker)
|
||||
.to_not have_enqueued_sidekiq_job
|
||||
expect(ActivityPub::StatusUpdateDistributionWorker)
|
||||
.to_not have_enqueued_sidekiq_job
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,7 +23,7 @@ RSpec.describe 'Share page', :js, :streaming do
|
|||
fill_in_form
|
||||
|
||||
expect(page)
|
||||
.to have_css('.notification-bar-message', text: frontend_translations('compose.published.body'))
|
||||
.to have_current_path(%r{/@bob/[0-9]+})
|
||||
end
|
||||
|
||||
def fill_in_form
|
||||
|
|
369
yarn.lock
369
yarn.lock
|
@ -1295,7 +1295,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@csstools/media-query-list-parser@npm:^4.0.2, @csstools/media-query-list-parser@npm:^4.0.3":
|
||||
"@csstools/media-query-list-parser@npm:^4.0.3":
|
||||
version: 4.0.3
|
||||
resolution: "@csstools/media-query-list-parser@npm:4.0.3"
|
||||
peerDependencies:
|
||||
|
@ -2262,17 +2262,14 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@formatjs/cli@npm:^6.1.1":
|
||||
version: 6.3.14
|
||||
resolution: "@formatjs/cli@npm:6.3.14"
|
||||
version: 6.7.2
|
||||
resolution: "@formatjs/cli@npm:6.7.2"
|
||||
peerDependencies:
|
||||
"@glimmer/env": ^0.1.7
|
||||
"@glimmer/reference": ^0.91.1 || ^0.92.0 || ^0.93.0
|
||||
"@glimmer/syntax": ^0.92.0 || ^0.93.0
|
||||
"@glimmer/validator": ^0.92.0 || ^0.93.0
|
||||
"@vue/compiler-core": ^3.4.0
|
||||
content-tag: ^2.0.1 || ^3.0.0
|
||||
ember-template-recast: ^6.1.4
|
||||
vue: ^3.4.0
|
||||
"@glimmer/syntax": ^0.94.9
|
||||
"@vue/compiler-core": ^3.5.12
|
||||
content-tag: ^3.0.0
|
||||
ember-template-recast: ^6.1.5
|
||||
vue: ^3.5.12
|
||||
peerDependenciesMeta:
|
||||
"@glimmer/env":
|
||||
optional: true
|
||||
|
@ -2292,7 +2289,7 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
formatjs: bin/formatjs
|
||||
checksum: 10c0/b4c83ed7fdc8dcd48b2f48fa9cca65b52472fb096eb028517a872f8a71ed3964f4b0a6bbc607f821a9504f396fe7341ef4d9ad44a381a37f280ed7547de66f41
|
||||
checksum: 10c0/fbcb1d35915a5b1542e4fa3f3f79e23fa5988681e6c238d7e8a3d4d32e18df3e5c36cebe01b04e011bb5c91f96f4d08d36af750259aa799d3e81943084e2f0c2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2595,12 +2592,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@keyv/serialize@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "@keyv/serialize@npm:1.0.3"
|
||||
dependencies:
|
||||
buffer: "npm:^6.0.3"
|
||||
checksum: 10c0/24a257870b0548cfe430680c2ae1641751e6a6ec90c573eaf51bfe956839b6cfa462b4d2827157363b6d620872d32d69fa2f37210a864ba488f8ec7158436398
|
||||
"@keyv/serialize@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "@keyv/serialize@npm:1.1.0"
|
||||
checksum: 10c0/30e34adf4fff52374c2c531e3ff215eed6414350ee56eebcb98c422feaff171b4900c73082a72399a6bfbc5ce60fbb6f968594110c960521923499146bc68c20
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2618,7 +2613,7 @@ __metadata:
|
|||
"@gamestdio/websocket": "npm:^0.3.2"
|
||||
"@github/webauthn-json": "npm:^2.1.1"
|
||||
"@optimize-lodash/rollup-plugin": "npm:^5.0.2"
|
||||
"@rails/ujs": "npm:7.1.501"
|
||||
"@rails/ujs": "npm:7.1.502"
|
||||
"@react-spring/web": "npm:^9.7.5"
|
||||
"@reduxjs/toolkit": "npm:^2.0.1"
|
||||
"@storybook/addon-a11y": "npm:^9.1.1"
|
||||
|
@ -2680,7 +2675,7 @@ __metadata:
|
|||
eslint-import-resolver-typescript: "npm:^4.2.5"
|
||||
eslint-plugin-formatjs: "npm:^5.3.1"
|
||||
eslint-plugin-import: "npm:~2.32.0"
|
||||
eslint-plugin-jsdoc: "npm:^53.0.0"
|
||||
eslint-plugin-jsdoc: "npm:^54.0.0"
|
||||
eslint-plugin-jsx-a11y: "npm:~6.10.2"
|
||||
eslint-plugin-promise: "npm:~7.2.1"
|
||||
eslint-plugin-react: "npm:^7.37.4"
|
||||
|
@ -3109,10 +3104,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rails/ujs@npm:7.1.501":
|
||||
version: 7.1.501
|
||||
resolution: "@rails/ujs@npm:7.1.501"
|
||||
checksum: 10c0/b75a30f36ff219264e0926da1ffcd14c2a5d6aee5be29da4dc81f9a45843875da79ac19cf7ed9a3f11a39084398d0ae4a75a8edb28ba94907db3081572af62b0
|
||||
"@rails/ujs@npm:7.1.502":
|
||||
version: 7.1.502
|
||||
resolution: "@rails/ujs@npm:7.1.502"
|
||||
checksum: 10c0/79b46e8abd03e3fc633d93cc4e4c23838c628b775802fb38c2ce68b0e609ce287a67b81db112a93cc78c07ec82ca3b4cf87e74eb556d35148ce5f64c8be9201f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -3476,37 +3471,37 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@storybook/addon-a11y@npm:^9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/addon-a11y@npm:9.1.1"
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/addon-a11y@npm:9.1.2"
|
||||
dependencies:
|
||||
"@storybook/global": "npm:^5.0.0"
|
||||
axe-core: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
storybook: ^9.1.1
|
||||
checksum: 10c0/bf5eba0a51ffec20c8c4432985494295115bcf48e0807e4ca21314845d4aaaaaae9122d4be4f78a2fc4c15caa5e1207c01e118724c2cbecbd80aa8a5f6826924
|
||||
storybook: ^9.1.2
|
||||
checksum: 10c0/36fc399db0af0acff6542c7e2aa54ef715dcff0e8a7f12fec3468dfdee2d83651c1d02c7226a420269d18f522dbaa96fa6faacb9c647c2a65518cece9d38582b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-docs@npm:^9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/addon-docs@npm:9.1.1"
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/addon-docs@npm:9.1.2"
|
||||
dependencies:
|
||||
"@mdx-js/react": "npm:^3.0.0"
|
||||
"@storybook/csf-plugin": "npm:9.1.1"
|
||||
"@storybook/csf-plugin": "npm:9.1.2"
|
||||
"@storybook/icons": "npm:^1.4.0"
|
||||
"@storybook/react-dom-shim": "npm:9.1.1"
|
||||
"@storybook/react-dom-shim": "npm:9.1.2"
|
||||
react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
ts-dedent: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
storybook: ^9.1.1
|
||||
checksum: 10c0/92b3ac089a38b892319de5ec02ca4ae477e38d88aef8561f6cbb1f15dd69ad856c194fca4514c783983baba063f428994a7d7f2aed98204b931f8e684e681194
|
||||
storybook: ^9.1.2
|
||||
checksum: 10c0/b17a3a8d3b9ad70f7cd8f8295f8cf7a10a6c39ab69e752f3acfb2260809055f85088a6382a2fc729b48860854b94a67faca239ff00bbe0e7e9553113cb2542fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-vitest@npm:^9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/addon-vitest@npm:9.1.1"
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/addon-vitest@npm:9.1.2"
|
||||
dependencies:
|
||||
"@storybook/global": "npm:^5.0.0"
|
||||
"@storybook/icons": "npm:^1.4.0"
|
||||
|
@ -3515,7 +3510,7 @@ __metadata:
|
|||
peerDependencies:
|
||||
"@vitest/browser": ^3.0.0
|
||||
"@vitest/runner": ^3.0.0
|
||||
storybook: ^9.1.1
|
||||
storybook: ^9.1.2
|
||||
vitest: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
"@vitest/browser":
|
||||
|
@ -3524,31 +3519,31 @@ __metadata:
|
|||
optional: true
|
||||
vitest:
|
||||
optional: true
|
||||
checksum: 10c0/a4770aad2f3e4ae10e3d7ae7083354e98287d095644aae87af62c59c9a97ec7e57cf25620c32e2e5f9261a3686a6efabea0a830061500f63e78a49a2bac6f130
|
||||
checksum: 10c0/75eacf6757d9ab6d0ad8c496d55a1548ab67f098a7ceb431900e8b6eb98ac8ac2235382a44a26765607be07e1b09c0e2a34ee9b846c234da6073d38aabc0ea4d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/builder-vite@npm:9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/builder-vite@npm:9.1.1"
|
||||
"@storybook/builder-vite@npm:9.1.2":
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/builder-vite@npm:9.1.2"
|
||||
dependencies:
|
||||
"@storybook/csf-plugin": "npm:9.1.1"
|
||||
"@storybook/csf-plugin": "npm:9.1.2"
|
||||
ts-dedent: "npm:^2.0.0"
|
||||
peerDependencies:
|
||||
storybook: ^9.1.1
|
||||
storybook: ^9.1.2
|
||||
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
checksum: 10c0/334235a64e05d6fb1e1cdf23f41ac211d1e55429e425e1aea33d9b4469503aa328eb5d9cad24e6328b392098afb2c884943d2c304c1eb3cf55ce25dcc3ad4414
|
||||
checksum: 10c0/2411e593903bc61336f2a2c6f48e7314dcc8c776346eff0f6fec28e9fc8e3a90d3f8d6561f30d1caf490349d34c7690f8addf4c56fa1fd778f0dfda49cf3aa97
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/csf-plugin@npm:9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/csf-plugin@npm:9.1.1"
|
||||
"@storybook/csf-plugin@npm:9.1.2":
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/csf-plugin@npm:9.1.2"
|
||||
dependencies:
|
||||
unplugin: "npm:^1.3.1"
|
||||
peerDependencies:
|
||||
storybook: ^9.1.1
|
||||
checksum: 10c0/d29b5685ef79eacbcd891977f95a58238f104004b014f88ee59eab6e5995df31010435aa222a27cdf54056accc43239c44f7e8e461c263c60b09d7d2383be8b8
|
||||
storybook: ^9.1.2
|
||||
checksum: 10c0/a145da545844b9b2af345d43d8f2c035dd801bd6414b4a9a2037dfa950250d08133a956226c49c36a79ffda171ad9388a0f1621c04cfed77e5c342817f4a275e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -3569,25 +3564,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react-dom-shim@npm:9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/react-dom-shim@npm:9.1.1"
|
||||
"@storybook/react-dom-shim@npm:9.1.2":
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/react-dom-shim@npm:9.1.2"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^9.1.1
|
||||
checksum: 10c0/ea2725719e04871b56c0a3755a7791034abd1d8ebb51392ed5f1eb2d21aee873444080509efe69c919e43121c299e06e2266d603478d059efe38762b0ae96ae5
|
||||
storybook: ^9.1.2
|
||||
checksum: 10c0/7547cb0fdcf8098c00017cbfb501f11a34ae73b9e13984520b8143e709b4b8ec1acf7fed9ce51dbb5b5af5dcd657396da17ef1f262f60efdd4956f3e26b3c704
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react-vite@npm:^9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/react-vite@npm:9.1.1"
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/react-vite@npm:9.1.2"
|
||||
dependencies:
|
||||
"@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.6.1"
|
||||
"@rollup/pluginutils": "npm:^5.0.2"
|
||||
"@storybook/builder-vite": "npm:9.1.1"
|
||||
"@storybook/react": "npm:9.1.1"
|
||||
"@storybook/builder-vite": "npm:9.1.2"
|
||||
"@storybook/react": "npm:9.1.2"
|
||||
find-up: "npm:^7.0.0"
|
||||
magic-string: "npm:^0.30.0"
|
||||
react-docgen: "npm:^8.0.0"
|
||||
|
@ -3596,27 +3591,27 @@ __metadata:
|
|||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^9.1.1
|
||||
storybook: ^9.1.2
|
||||
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
checksum: 10c0/fa79e5a9be38f229a8cc0db85452e651081740cd3f9e6951c0fc3e195437124f8a792494b90a613c673ef9410d565d5f459276e157f073367ea28f0293d10167
|
||||
checksum: 10c0/afed36a0219599577b255042a9c9ac1af0106003ac37e2e9b5846a42b4e8729ff0e8b7ae6018d3ac85b69e918c2a20d554cd484de7345e5fb4974df92914e059
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react@npm:9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "@storybook/react@npm:9.1.1"
|
||||
"@storybook/react@npm:9.1.2":
|
||||
version: 9.1.2
|
||||
resolution: "@storybook/react@npm:9.1.2"
|
||||
dependencies:
|
||||
"@storybook/global": "npm:^5.0.0"
|
||||
"@storybook/react-dom-shim": "npm:9.1.1"
|
||||
"@storybook/react-dom-shim": "npm:9.1.2"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^9.1.1
|
||||
storybook: ^9.1.2
|
||||
typescript: ">= 4.9.x"
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10c0/343598e9c1bb2e53b7d6eaaa50b5c024c109fb825f6f4869a82da0ccd428400e5a93eba494e810f6bfbf6bff9b73810595ab8ffdd1530df8b4f2b20e617623e3
|
||||
checksum: 10c0/ea3d9fa25825fde5022942579db9a57154e57cb37244b0d54bb189679a37f20c20906041898f5fcfd4867043ea789384c2d968f334f9d0c55958add0b18fb6ea
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -5402,13 +5397,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"base64-js@npm:^1.3.1":
|
||||
version: 1.5.1
|
||||
resolution: "base64-js@npm:1.5.1"
|
||||
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"better-opn@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "better-opn@npm:3.0.2"
|
||||
|
@ -5528,16 +5516,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"buffer@npm:^6.0.3":
|
||||
version: 6.0.3
|
||||
resolution: "buffer@npm:6.0.3"
|
||||
dependencies:
|
||||
base64-js: "npm:^1.3.1"
|
||||
ieee754: "npm:^1.2.1"
|
||||
checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bufferutil@npm:^4.0.7":
|
||||
version: 4.0.9
|
||||
resolution: "bufferutil@npm:4.0.9"
|
||||
|
@ -5582,13 +5560,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacheable@npm:^1.9.0":
|
||||
version: 1.9.0
|
||||
resolution: "cacheable@npm:1.9.0"
|
||||
"cacheable@npm:^1.10.3":
|
||||
version: 1.10.3
|
||||
resolution: "cacheable@npm:1.10.3"
|
||||
dependencies:
|
||||
hookified: "npm:^1.8.2"
|
||||
keyv: "npm:^5.3.3"
|
||||
checksum: 10c0/1ca171dd2f7c3a929d84b3f94e712cb8fbbfb7c636f19ba01657cf89c6ea4316f21f2beb6c696fa5c87d42f52620f4a7c2dfecb41bf71ee3b249d539895256c4
|
||||
hookified: "npm:^1.10.0"
|
||||
keyv: "npm:^5.4.0"
|
||||
checksum: 10c0/eaa483140133b58dbd5c9811688137016c263a874886ce98f9590d252fb92859633929b36aa4c05ec67aee70cc1c9ba9aa1be02e53365604dd0202a88e44fef8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -5678,10 +5656,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chalk@npm:^5.4.1":
|
||||
version: 5.4.1
|
||||
resolution: "chalk@npm:5.4.1"
|
||||
checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef
|
||||
"chalk@npm:^5.5.0":
|
||||
version: 5.5.0
|
||||
resolution: "chalk@npm:5.5.0"
|
||||
checksum: 10c0/23063b544f7c2fe57d25ff814807de561f8adfff72e4f0051051eaa606f772586470507ccd38d89166300eeaadb0164acde8bb8a0716a0f2d56ccdf3761d5e4f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -5864,10 +5842,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^13.1.0":
|
||||
version: 13.1.0
|
||||
resolution: "commander@npm:13.1.0"
|
||||
checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164
|
||||
"commander@npm:^14.0.0":
|
||||
version: 14.0.0
|
||||
resolution: "commander@npm:14.0.0"
|
||||
checksum: 10c0/73c4babfa558077868d84522b11ef56834165d472b9e86a634cd4c3ae7fc72d59af6377d8878e06bd570fe8f3161eced3cbe383c38f7093272bb65bd242b595b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -6217,7 +6195,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.1":
|
||||
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.1":
|
||||
version: 4.4.1
|
||||
resolution: "debug@npm:4.4.1"
|
||||
dependencies:
|
||||
|
@ -6986,9 +6964,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-jsdoc@npm:^53.0.0":
|
||||
version: 53.0.1
|
||||
resolution: "eslint-plugin-jsdoc@npm:53.0.1"
|
||||
"eslint-plugin-jsdoc@npm:^54.0.0":
|
||||
version: 54.0.0
|
||||
resolution: "eslint-plugin-jsdoc@npm:54.0.0"
|
||||
dependencies:
|
||||
"@es-joy/jsdoccomment": "npm:~0.52.0"
|
||||
are-docs-informative: "npm:^0.0.2"
|
||||
|
@ -7002,7 +6980,7 @@ __metadata:
|
|||
spdx-expression-parse: "npm:^4.0.0"
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
|
||||
checksum: 10c0/d629863faa98026edc09535e8095669d97be4e77173bfe6cbe7ea0fcd641cd2537bec58e25db433cc6f4103d9a2a13c1bbb3916a8ed4ff05b18c566971dec472
|
||||
checksum: 10c0/cf0a388fc670ababe26f9584c467bc8c1592aa83affcf16118d8181c186a6d8f02a8ea65250766b45168fca5cb879a6af66e8457cdb98f0f923bd927572e2de5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -7309,9 +7287,9 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"fake-indexeddb@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "fake-indexeddb@npm:6.0.1"
|
||||
checksum: 10c0/60f4ccdfd5ecb37bb98019056c688366847840cce7146e0005c5ca54823238455403b0a8803b898a11cf80f6147b1bb553457c6af427a644a6e64566cdbe42ec
|
||||
version: 6.1.0
|
||||
resolution: "fake-indexeddb@npm:6.1.0"
|
||||
checksum: 10c0/f2bae6cf3ed38619ccc536ee1c0d72a1ba721da24d840e9c0993800c031a5c1d8e61adc00ea31f0c2a2380447c57bc953bd4128b08dabf559c7cdf03c8893239
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -7412,12 +7390,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-entry-cache@npm:^10.0.8":
|
||||
version: 10.1.0
|
||||
resolution: "file-entry-cache@npm:10.1.0"
|
||||
"file-entry-cache@npm:^10.1.3":
|
||||
version: 10.1.3
|
||||
resolution: "file-entry-cache@npm:10.1.3"
|
||||
dependencies:
|
||||
flat-cache: "npm:^6.1.9"
|
||||
checksum: 10c0/9464848577f68809237ffdf11c09a285d930ed4cda8cf392ee44ac8fa70658e41bbe60d08d883285d6af798a26f008dd8dfa94a31d42ecf1ba5a7bcd6dd8b07d
|
||||
flat-cache: "npm:^6.1.12"
|
||||
checksum: 10c0/7365c3358698f5ccf085c164989ad48f1d9341157895577d7c34bf4f9c258d2410f4d2c749c73232111aab9e2fdd632ef6941f2c2d3acdd3a7f3daf2c840bd54
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -7501,14 +7479,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flat-cache@npm:^6.1.9":
|
||||
version: 6.1.9
|
||||
resolution: "flat-cache@npm:6.1.9"
|
||||
"flat-cache@npm:^6.1.12":
|
||||
version: 6.1.12
|
||||
resolution: "flat-cache@npm:6.1.12"
|
||||
dependencies:
|
||||
cacheable: "npm:^1.9.0"
|
||||
cacheable: "npm:^1.10.3"
|
||||
flatted: "npm:^3.3.3"
|
||||
hookified: "npm:^1.8.2"
|
||||
checksum: 10c0/ca9241fab68154e9a4efe8875beff43cb7b2de65628d15dcf8488dc69bca3f8e98085a707c3395d03b1022f586364b0f37aa5dd5cc085a8cf7481516757ac864
|
||||
hookified: "npm:^1.10.0"
|
||||
checksum: 10c0/9c7e22ebc68edef373170a2171fe4d7d68eecd18953fbd16f5f3e9c32c36491b61ab0468e07242a5bbed58b36d139a41d3c33b23fc013fc535a41b00546c14f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -8021,10 +7999,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hookified@npm:^1.8.2":
|
||||
version: 1.9.0
|
||||
resolution: "hookified@npm:1.9.0"
|
||||
checksum: 10c0/28145882504e40ef58f328554966520c56dc2aaca92457996a5cd68fda6f92e38d3ca283e7ebbf3d71f36cda887234ce580abfa6532ade906be7810a812f15fa
|
||||
"hookified@npm:^1.10.0":
|
||||
version: 1.11.0
|
||||
resolution: "hookified@npm:1.11.0"
|
||||
checksum: 10c0/c74d28e90c55247ffc036a5cabd0681e715f50db8c6b1f47e10253b577e355f3dcd71bb96565a23467f72a8695ec2d482e5801e2d9d99ac24bdc179fef635ba0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -8146,13 +8124,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ieee754@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "ieee754@npm:1.2.1"
|
||||
checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ignore@npm:^5.2.0":
|
||||
version: 5.3.2
|
||||
resolution: "ignore@npm:5.3.2"
|
||||
|
@ -8160,7 +8131,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ignore@npm:^7.0.0, ignore@npm:^7.0.3":
|
||||
"ignore@npm:^7.0.0, ignore@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "ignore@npm:7.0.5"
|
||||
checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d
|
||||
|
@ -8986,12 +8957,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"keyv@npm:^5.3.3":
|
||||
version: 5.3.3
|
||||
resolution: "keyv@npm:5.3.3"
|
||||
"keyv@npm:^5.4.0":
|
||||
version: 5.5.0
|
||||
resolution: "keyv@npm:5.5.0"
|
||||
dependencies:
|
||||
"@keyv/serialize": "npm:^1.0.3"
|
||||
checksum: 10c0/6b9064d061784e80a5dc500453b03cacb099f06fddd0346063519371d563a66771237e04467f3387b60d8a33a3c99864288991274921fb1338c6adf638574924
|
||||
"@keyv/serialize": "npm:^1.1.0"
|
||||
checksum: 10c0/2db63fd2abcdf71929f032569673b6edd0de111edb012411658e2589dc5f49793a98aecd56c67fafda3f90a31f32e35555a97f8621040728260c66ad8daeea48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -9016,6 +8987,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"known-css-properties@npm:^0.37.0":
|
||||
version: 0.37.0
|
||||
resolution: "known-css-properties@npm:0.37.0"
|
||||
checksum: 10c0/e0ec08cae580e8833254b690971f73ec6f78ac461820a3c755b4a0b62c5b871501753b4aa60b30576a0f621ba44b231235cf9f35ab89e2e7de5448dfe0600241
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lande@npm:^1.0.10":
|
||||
version: 1.0.10
|
||||
resolution: "lande@npm:1.0.10"
|
||||
|
@ -9073,28 +9051,28 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"lint-staged@npm:^16.0.0":
|
||||
version: 16.0.0
|
||||
resolution: "lint-staged@npm:16.0.0"
|
||||
version: 16.1.5
|
||||
resolution: "lint-staged@npm:16.1.5"
|
||||
dependencies:
|
||||
chalk: "npm:^5.4.1"
|
||||
commander: "npm:^13.1.0"
|
||||
debug: "npm:^4.4.0"
|
||||
chalk: "npm:^5.5.0"
|
||||
commander: "npm:^14.0.0"
|
||||
debug: "npm:^4.4.1"
|
||||
lilconfig: "npm:^3.1.3"
|
||||
listr2: "npm:^8.3.3"
|
||||
listr2: "npm:^9.0.1"
|
||||
micromatch: "npm:^4.0.8"
|
||||
nano-spawn: "npm:^1.0.0"
|
||||
nano-spawn: "npm:^1.0.2"
|
||||
pidtree: "npm:^0.6.0"
|
||||
string-argv: "npm:^0.3.2"
|
||||
yaml: "npm:^2.7.1"
|
||||
yaml: "npm:^2.8.1"
|
||||
bin:
|
||||
lint-staged: bin/lint-staged.js
|
||||
checksum: 10c0/8778dbe7892bbf14e378d612d1649c1e3df38a8ddf14cf35962b6e8a962be72efb1ebb48a697e38366be97d25b8d2599cad3c26ac5afc0d0460452484e27924d
|
||||
checksum: 10c0/771e7be871f1d74ed09ef4e4eae5f835ed962965db7709be26cccf71bef8fed34f8d5d92f193b2a6fad32c12d955850aa74008e6180fabea8a7a6666cba2ac39
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"listr2@npm:^8.3.3":
|
||||
version: 8.3.3
|
||||
resolution: "listr2@npm:8.3.3"
|
||||
"listr2@npm:^9.0.1":
|
||||
version: 9.0.1
|
||||
resolution: "listr2@npm:9.0.1"
|
||||
dependencies:
|
||||
cli-truncate: "npm:^4.0.0"
|
||||
colorette: "npm:^2.0.20"
|
||||
|
@ -9102,7 +9080,7 @@ __metadata:
|
|||
log-update: "npm:^6.1.0"
|
||||
rfdc: "npm:^1.4.1"
|
||||
wrap-ansi: "npm:^9.0.0"
|
||||
checksum: 10c0/0792f8a7fd482fa516e21689e012e07081cab3653172ca606090622cfa0024c784a1eba8095a17948a0e9a4aa98a80f7c9c90f78a0dd35173d6802f9cc123a82
|
||||
checksum: 10c0/73462e84a3c4f05de5a3cdea5eaa0209c6ab04a2fdb4046545049806e9ba17b6ee84a097ebf7ffc0e903b0f2a9094c0c480cd2f2bb21d7d21e20969e17a3c32b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -9588,8 +9566,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"msw@npm:^2.10.2":
|
||||
version: 2.10.2
|
||||
resolution: "msw@npm:2.10.2"
|
||||
version: 2.10.4
|
||||
resolution: "msw@npm:2.10.4"
|
||||
dependencies:
|
||||
"@bundled-es-modules/cookie": "npm:^2.0.1"
|
||||
"@bundled-es-modules/statuses": "npm:^1.0.1"
|
||||
|
@ -9616,7 +9594,7 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
msw: cli/index.js
|
||||
checksum: 10c0/fb44961e17e12864b4764b4c015f6ce7c907081f8dcd237ecd635eab00b787847406fbd36a2bcf2ef4c21114a3610ac03c7f93f3080f509a69b0c1c5285fd683
|
||||
checksum: 10c0/48dff36c7cf8ad504bb8f8a2ff6946cf5727752c140681eb68da00991d9fe56224bace970476771a9fffae136256c389c591d71368a6967d053dbad6b6df3346
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -9627,10 +9605,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nano-spawn@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "nano-spawn@npm:1.0.1"
|
||||
checksum: 10c0/e03edc6971f653bc4651f2413b2011772a7c18797c0a4e986ff8eaea3adf4f017697d4d494ffb4ba6bce907b42abbeb0f7f681dbf336c84a324c940fb64c1dec
|
||||
"nano-spawn@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "nano-spawn@npm:1.0.2"
|
||||
checksum: 10c0/d8cec78f127a44aa5e38be01746b3d963a8dcf8b00b4a05bf259b5369af2225b8c7dc9d12517050b90234e5c3eeea4ece5d18a5f9c6c3462b56f9f595f07e632
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -10278,8 +10256,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"pino-pretty@npm:^13.0.0":
|
||||
version: 13.0.0
|
||||
resolution: "pino-pretty@npm:13.0.0"
|
||||
version: 13.1.1
|
||||
resolution: "pino-pretty@npm:13.1.1"
|
||||
dependencies:
|
||||
colorette: "npm:^2.0.7"
|
||||
dateformat: "npm:^4.6.3"
|
||||
|
@ -10291,12 +10269,12 @@ __metadata:
|
|||
on-exit-leak-free: "npm:^2.1.0"
|
||||
pino-abstract-transport: "npm:^2.0.0"
|
||||
pump: "npm:^3.0.0"
|
||||
secure-json-parse: "npm:^2.4.0"
|
||||
secure-json-parse: "npm:^4.0.0"
|
||||
sonic-boom: "npm:^4.0.1"
|
||||
strip-json-comments: "npm:^3.1.1"
|
||||
strip-json-comments: "npm:^5.0.2"
|
||||
bin:
|
||||
pino-pretty: bin.js
|
||||
checksum: 10c0/015dac25006c1b9820b9e01fccb8a392a019e12b30e6bfc3f3f61ecca8dbabcd000a8f3f64410b620b7f5d08579ba85e6ef137f7fbeaad70d46397a97a5f75ea
|
||||
checksum: 10c0/845c07afd3d73cb96ad2049cfa7fca12b8280a51e30d6db8b490857690637556bb8e7f05b2fa640b3e4a7edd9b1369110042d670fda743ef98fe3be29876c8c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -10328,27 +10306,27 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright-core@npm:1.54.1":
|
||||
version: 1.54.1
|
||||
resolution: "playwright-core@npm:1.54.1"
|
||||
"playwright-core@npm:1.54.2":
|
||||
version: 1.54.2
|
||||
resolution: "playwright-core@npm:1.54.2"
|
||||
bin:
|
||||
playwright-core: cli.js
|
||||
checksum: 10c0/b821262b024d7753b1bfa71eb2bc99f2dda12a869d175b2e1bc6ac2764bd661baf36d9d42f45caf622854ad7e4a6077b9b57014c74bb5a78fe339c9edf1c9019
|
||||
checksum: 10c0/44850e20bf35237c8c3dedf1096c655f8af939dde53c5469f72cae3dd744966858a302419b909a73d7a2093323123e7ebcc0fdd55151b4193afb7812c1fd2c88
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright@npm:^1.54.1":
|
||||
version: 1.54.1
|
||||
resolution: "playwright@npm:1.54.1"
|
||||
version: 1.54.2
|
||||
resolution: "playwright@npm:1.54.2"
|
||||
dependencies:
|
||||
fsevents: "npm:2.3.2"
|
||||
playwright-core: "npm:1.54.1"
|
||||
playwright-core: "npm:1.54.2"
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 10c0/c5fedae31a03a1f4c4846569aef3ffb98da23000a4d255abfc8c2ede15b43cc7cd87b80f6fa078666c030373de8103787cf77ef7653ae9458aabbbd4320c2599
|
||||
checksum: 10c0/6f642fa70179eee5d5bf8a90df2a6147c9638ff926f4f3ad0a0517396b8a3fe00ccebf13377e032a75b3f0b2610ec1562293e0cfc3bde234181c7a50af8af80a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -10772,7 +10750,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.5.3, postcss@npm:^8.5.6":
|
||||
"postcss@npm:^8.5.6":
|
||||
version: 8.5.6
|
||||
resolution: "postcss@npm:8.5.6"
|
||||
dependencies:
|
||||
|
@ -10821,11 +10799,11 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"prettier@npm:^3.3.3":
|
||||
version: 3.4.2
|
||||
resolution: "prettier@npm:3.4.2"
|
||||
version: 3.6.2
|
||||
resolution: "prettier@npm:3.6.2"
|
||||
bin:
|
||||
prettier: bin/prettier.cjs
|
||||
checksum: 10c0/99e076a26ed0aba4ebc043880d0f08bbb8c59a4c6641cdee6cdadf2205bdd87aa1d7823f50c3aea41e015e99878d37c58d7b5f0e663bba0ef047f94e36b96446
|
||||
checksum: 10c0/488cb2f2b99ec13da1e50074912870217c11edaddedeadc649b1244c749d15ba94e846423d062e2c4c9ae683e2d65f754de28889ba06e697ac4f988d44f45812
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -12030,10 +12008,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"secure-json-parse@npm:^2.4.0":
|
||||
version: 2.7.0
|
||||
resolution: "secure-json-parse@npm:2.7.0"
|
||||
checksum: 10c0/f57eb6a44a38a3eeaf3548228585d769d788f59007454214fab9ed7f01fbf2e0f1929111da6db28cf0bcc1a2e89db5219a59e83eeaec3a54e413a0197ce879e4
|
||||
"secure-json-parse@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "secure-json-parse@npm:4.0.0"
|
||||
checksum: 10c0/1a298cf00e1de91e833cee5eb406d6e77fb2f7eca9bef3902047d49e7f5d3e6c21b5de61ff73466c831e716430bfe87d732a6e645a7dabb5f1e8a8e4d3e15eb4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -12527,8 +12505,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"storybook@npm:^9.1.1":
|
||||
version: 9.1.1
|
||||
resolution: "storybook@npm:9.1.1"
|
||||
version: 9.1.2
|
||||
resolution: "storybook@npm:9.1.2"
|
||||
dependencies:
|
||||
"@storybook/global": "npm:^5.0.0"
|
||||
"@testing-library/jest-dom": "npm:^6.6.3"
|
||||
|
@ -12549,7 +12527,7 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
storybook: ./bin/index.cjs
|
||||
checksum: 10c0/efd2665547dc6bfd4cabd00ee1a6368c7cec27a3e4c16bc875ae06bb5d9ee74120c11102699165530f31fe8becb47212d80632b1fefcafa60fef94b3cd0bf50f
|
||||
checksum: 10c0/3a575f94913f9000a3591e5c685f4eabf75fa78ce306f8b0d48e9c72e46028df31f6d15955b8a338be2bf48dadca6550b65782783d8b3cb4b737ba9f3887d007
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -12757,6 +12735,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-json-comments@npm:^5.0.2":
|
||||
version: 5.0.3
|
||||
resolution: "strip-json-comments@npm:5.0.3"
|
||||
checksum: 10c0/daaf20b29f69fb51112698f4a9a662490dbb78d5baf6127c75a0a83c2ac6c078a8c0f74b389ad5e0519d6fc359c4a57cb9971b1ae201aef62ce45a13247791e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-literal@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "strip-literal@npm:3.0.0"
|
||||
|
@ -12850,12 +12835,12 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"stylelint@npm:^16.19.1":
|
||||
version: 16.19.1
|
||||
resolution: "stylelint@npm:16.19.1"
|
||||
version: 16.23.1
|
||||
resolution: "stylelint@npm:16.23.1"
|
||||
dependencies:
|
||||
"@csstools/css-parser-algorithms": "npm:^3.0.4"
|
||||
"@csstools/css-tokenizer": "npm:^3.0.3"
|
||||
"@csstools/media-query-list-parser": "npm:^4.0.2"
|
||||
"@csstools/css-parser-algorithms": "npm:^3.0.5"
|
||||
"@csstools/css-tokenizer": "npm:^3.0.4"
|
||||
"@csstools/media-query-list-parser": "npm:^4.0.3"
|
||||
"@csstools/selector-specificity": "npm:^5.0.0"
|
||||
"@dual-bundle/import-meta-resolve": "npm:^4.1.0"
|
||||
balanced-match: "npm:^2.0.0"
|
||||
|
@ -12863,24 +12848,24 @@ __metadata:
|
|||
cosmiconfig: "npm:^9.0.0"
|
||||
css-functions-list: "npm:^3.2.3"
|
||||
css-tree: "npm:^3.1.0"
|
||||
debug: "npm:^4.3.7"
|
||||
debug: "npm:^4.4.1"
|
||||
fast-glob: "npm:^3.3.3"
|
||||
fastest-levenshtein: "npm:^1.0.16"
|
||||
file-entry-cache: "npm:^10.0.8"
|
||||
file-entry-cache: "npm:^10.1.3"
|
||||
global-modules: "npm:^2.0.0"
|
||||
globby: "npm:^11.1.0"
|
||||
globjoin: "npm:^0.1.4"
|
||||
html-tags: "npm:^3.3.1"
|
||||
ignore: "npm:^7.0.3"
|
||||
ignore: "npm:^7.0.5"
|
||||
imurmurhash: "npm:^0.1.4"
|
||||
is-plain-object: "npm:^5.0.0"
|
||||
known-css-properties: "npm:^0.36.0"
|
||||
known-css-properties: "npm:^0.37.0"
|
||||
mathml-tag-names: "npm:^2.1.3"
|
||||
meow: "npm:^13.2.0"
|
||||
micromatch: "npm:^4.0.8"
|
||||
normalize-path: "npm:^3.0.0"
|
||||
picocolors: "npm:^1.1.1"
|
||||
postcss: "npm:^8.5.3"
|
||||
postcss: "npm:^8.5.6"
|
||||
postcss-resolve-nested-selector: "npm:^0.1.6"
|
||||
postcss-safe-parser: "npm:^7.0.1"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
|
@ -12893,7 +12878,7 @@ __metadata:
|
|||
write-file-atomic: "npm:^5.0.1"
|
||||
bin:
|
||||
stylelint: bin/stylelint.mjs
|
||||
checksum: 10c0/e633f323ff730e8f2ac982067e4caa9a6c98b81a519e7adff96fa7a7d047f68a24c0dd2d81f3511b0943d99c915f20f19da911d16d47336705ea70d46e960c89
|
||||
checksum: 10c0/18d01587396cce68b59e4a89a7c89d5eb7e76ee7cc27dd109b0f8f241625eb0ffe87763f67b2d20df0f23a243443591fa2514300311a48a945bd6a3bc14db36b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -14545,12 +14530,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yaml@npm:^2.7.1":
|
||||
version: 2.8.0
|
||||
resolution: "yaml@npm:2.8.0"
|
||||
"yaml@npm:^2.8.1":
|
||||
version: 2.8.1
|
||||
resolution: "yaml@npm:2.8.1"
|
||||
bin:
|
||||
yaml: bin.mjs
|
||||
checksum: 10c0/f6f7310cf7264a8107e72c1376f4de37389945d2fb4656f8060eca83f01d2d703f9d1b925dd8f39852a57034fafefde6225409ddd9f22aebfda16c6141b71858
|
||||
checksum: 10c0/7c587be00d9303d2ae1566e03bc5bc7fe978ba0d9bf39cc418c3139d37929dfcb93a230d9749f2cb578b6aa5d9ebebc322415e4b653cb83acd8bc0bc321707f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user