1
0
mirror of https://github.com/funamitech/mastodon synced 2024-11-30 15:58:28 +09:00
This commit is contained in:
Noa Himesaka 2024-10-17 00:30:51 +09:00
commit d45671066c
94 changed files with 902 additions and 675 deletions

View File

@ -21,9 +21,11 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- id: version_vars - id: version_vars
run: | run: |
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT
echo mastodon_short_sha=$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT
outputs: outputs:
metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }} metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }}
short_sha: ${{ steps.version_vars.outputs.mastodon_short_sha }}
build-image: build-image:
needs: compute-suffix needs: compute-suffix
@ -39,6 +41,7 @@ jobs:
latest=auto latest=auto
tags: | tags: |
type=ref,event=pr type=ref,event=pr
type=ref,event=pr,suffix=-${{ needs.compute-suffix.outputs.short_sha }}
secrets: inherit secrets: inherit
build-image-streaming: build-image-streaming:
@ -55,4 +58,5 @@ jobs:
latest=auto latest=auto
tags: | tags: |
type=ref,event=pr type=ref,event=pr
type=ref,event=pr,suffix=-${{ needs.compute-suffix.outputs.short_sha }}
secrets: inherit secrets: inherit

View File

@ -32,7 +32,7 @@ jobs:
labels: | labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes org.opencontainers.image.description=Nightly build image used for testing purposes
flavor: | flavor: |
latest=true latest=auto
tags: | tags: |
type=raw,value=edge type=raw,value=edge
type=raw,value=nightly type=raw,value=nightly
@ -53,7 +53,7 @@ jobs:
labels: | labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes org.opencontainers.image.description=Nightly build image used for testing purposes
flavor: | flavor: |
latest=true latest=auto
tags: | tags: |
type=raw,value=edge type=raw,value=edge
type=raw,value=nightly type=raw,value=nightly

View File

@ -2,7 +2,7 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.3.0] - UNRELEASED ## [4.3.0] - 2024-10-08
The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski. The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski.
@ -11,12 +11,12 @@ The following changelog entries focus on changes visible to users, administrator
- **Add confirmation interstitial instead of silently redirecting logged-out visitors to remote resources** (#27792, #28902, and #30651 by @ClearlyClaire and @Gargron)\ - **Add confirmation interstitial instead of silently redirecting logged-out visitors to remote resources** (#27792, #28902, and #30651 by @ClearlyClaire and @Gargron)\
This fixes a longstanding open redirect in Mastodon, at the cost of added friction when local links to remote resources are shared. This fixes a longstanding open redirect in Mastodon, at the cost of added friction when local links to remote resources are shared.
- Fix ReDoS vulnerability on some Ruby versions ([GHSA-jpxp-r43f-rhvx](https://github.com/mastodon/mastodon/security/advisories/GHSA-jpxp-r43f-rhvx)) - Fix ReDoS vulnerability on some Ruby versions ([GHSA-jpxp-r43f-rhvx](https://github.com/mastodon/mastodon/security/advisories/GHSA-jpxp-r43f-rhvx))
- Change `form-action` Content-Security-Policy directive to be more restrictive (#26897 by @ClearlyClaire) - Change `form-action` Content-Security-Policy directive to be more restrictive (#26897 and #32241 by @ClearlyClaire)
- Update dependencies - Update dependencies
### Added ### Added
- **Add server-side notification grouping** (#29889, #30576, #30685, #30688, #30707, #30776, #30779, #30781, #30440, #31062, #31098, #31076, #31111, #31123, #31223, #31214, #31224, #31299, #31325, #31347, #31304, #31326, #31384, #31403, #31433, #31509, #31486, #31513, #31592, #31594, #31638, #31746, #31652, #31709, #31725, #31745, #31613, #31657, #31840, #31610, #31929, #32089 and #32085 by @ClearlyClaire, @Gargron, @mgmn, and @renchap)\ - **Add server-side notification grouping** (#29889, #30576, #30685, #30688, #30707, #30776, #30779, #30781, #30440, #31062, #31098, #31076, #31111, #31123, #31223, #31214, #31224, #31299, #31325, #31347, #31304, #31326, #31384, #31403, #31433, #31509, #31486, #31513, #31592, #31594, #31638, #31746, #31652, #31709, #31725, #31745, #31613, #31657, #31840, #31610, #31929, #32089, #32085, #32243, #32179 and #32254 by @ClearlyClaire, @Gargron, @mgmn, and @renchap)\
Group notifications of the same type for the same target, so that your notifications no longer get cluttered by boost and favorite notifications as soon as a couple of your posts get traction.\ Group notifications of the same type for the same target, so that your notifications no longer get cluttered by boost and favorite notifications as soon as a couple of your posts get traction.\
This is done server-side so that clients can efficiently get relevant groups without having to go through numerous pages of individual notifications.\ This is done server-side so that clients can efficiently get relevant groups without having to go through numerous pages of individual notifications.\
As part of this, the visual design of the entire notifications feature has been revamped.\ As part of this, the visual design of the entire notifications feature has been revamped.\
@ -28,7 +28,7 @@ The following changelog entries focus on changes visible to users, administrator
- `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts - `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts
- `POST /api/v2/notifications/:group_key/dimsiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group - `POST /api/v2/notifications/:group_key/dimsiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
- `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count - `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count
- **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723 and #32062 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\ - **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723, #32062 and #32281 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\
The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\ The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\
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.\ 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.\ 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.\
@ -61,7 +61,7 @@ The following changelog entries focus on changes visible to users, administrator
- **Add timeline of public posts about a trending link** (#30381 and #30840 by @Gargron)\ - **Add timeline of public posts about a trending link** (#30381 and #30840 by @Gargron)\
You can now see public posts mentioning currently-trending articles from people who have opted into discovery features.\ You can now see public posts mentioning currently-trending articles from people who have opted into discovery features.\
This adds a new REST API endpoint: https://docs.joinmastodon.org/methods/timelines/#link This adds a new REST API endpoint: https://docs.joinmastodon.org/methods/timelines/#link
- **Add author highlight for news articles whose authors are on the fediverse** (#30398, #30670, #30521, #30846, #31819, and #31900 by @Gargron and @oneiros)\ - **Add author highlight for news articles whose authors are on the fediverse** (#30398, #30670, #30521, #30846, #31819, #31900 and #32188 by @Gargron, @mjankowski and @oneiros)\
This adds a mechanism to [highlight the author of news articles](https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/) shared on Mastodon.\ This adds a mechanism to [highlight the author of news articles](https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/) shared on Mastodon.\
Articles hosted outside the fediverse can indicate a fediverse author with a meta tag: Articles hosted outside the fediverse can indicate a fediverse author with a meta tag:
```html ```html
@ -150,10 +150,12 @@ The following changelog entries focus on changes visible to users, administrator
- Add groundwork for annual reports for accounts (#28693 by @Gargron)\ - Add groundwork for annual reports for accounts (#28693 by @Gargron)\
This lays the groundwork for a “year-in-review”/“wrapped” style report for local users, but is currently not in use. This lays the groundwork for a “year-in-review”/“wrapped” style report for local users, but is currently not in use.
- Add notification email on invalid second authenticator (#28822 by @ClearlyClaire) - Add notification email on invalid second authenticator (#28822 by @ClearlyClaire)
- Add date of account deletion in list of accounts in the admin interface (#25640 by @tribela)
- Add new emojis from `jdecked/twemoji` 15.0 (#28404 by @TheEssem) - Add new emojis from `jdecked/twemoji` 15.0 (#28404 by @TheEssem)
- Add configurable error handling in attachment batch deletion (#28184 by @vmstan)\ - Add configurable error handling in attachment batch deletion (#28184 by @vmstan)\
This makes the S3 batch size configurable through the `S3_BATCH_DELETE_LIMIT` environment variable (defaults to 1000), and adds some retry logic, configurable through the `S3_BATCH_DELETE_RETRY` environment variable (defaults to 3). This makes the S3 batch size configurable through the `S3_BATCH_DELETE_LIMIT` environment variable (defaults to 1000), and adds some retry logic, configurable through the `S3_BATCH_DELETE_RETRY` environment variable (defaults to 3).
- Add VAPID public key to instance serializer (#28006 by @ThisIsMissEm) - Add VAPID public key to instance serializer (#28006 by @ThisIsMissEm)
- Add support for serving JRD `/.well-known/host-meta.json` in addition to XRD host-meta (#32206 by @c960657)
- Add `nodeName` and `nodeDescription` to nodeinfo `metadata` (#28079 by @6543) - Add `nodeName` and `nodeDescription` to nodeinfo `metadata` (#28079 by @6543)
- Add Thai diacritics and tone marks in `HASHTAG_INVALID_CHARS_RE` (#26576 by @ppnplus) - Add Thai diacritics and tone marks in `HASHTAG_INVALID_CHARS_RE` (#26576 by @ppnplus)
- Add variable delay before link verification of remote account links (#27774 by @ClearlyClaire) - Add variable delay before link verification of remote account links (#27774 by @ClearlyClaire)
@ -168,7 +170,7 @@ The following changelog entries focus on changes visible to users, administrator
### Changed ### Changed
- **Change icons throughout the web interface** (#27385, #27539, #27555, #27579, #27700, #27817, #28519, #28709, #28064, #28775, #28780, #27924, #29294, #29395, #29537, #29569, #29610, #29612, #29649, #29844, #27780, #30974, #30963, #30962, #30961, #31362, #31363, #31359, #31371, #31360, #31512, #31511, and #31525 by @ClearlyClaire, @Gargron, @arbolitoloco1, @mjankowski, @nclm, @renchap, @ronilaukkarinen, and @zunda)\ - **Change icons throughout the web interface** (#27385, #27539, #27555, #27579, #27700, #27817, #28519, #28709, #28064, #28775, #28780, #27924, #29294, #29395, #29537, #29569, #29610, #29612, #29649, #29844, #27780, #30974, #30963, #30962, #30961, #31362, #31363, #31359, #31371, #31360, #31512, #31511, #31525, #32153, and #32201 by @ClearlyClaire, @Gargron, @arbolitoloco1, @mjankowski, @nclm, @renchap, @ronilaukkarinen, and @zunda)\
This changes all the interface icons from FontAwesome to Material Symbols for a more modern look, consistent with the official Mastodon Android app.\ This changes all the interface icons from FontAwesome to Material Symbols for a more modern look, consistent with the official Mastodon Android app.\
In addition, better care is given to pixel alignment, and icon variants are used to better highlight active/inactive state. In addition, better care is given to pixel alignment, and icon variants are used to better highlight active/inactive state.
- **Change design of compose form in web UI** (#28119, #29059, #29248, #29372, #29384, #29417, #29456, #29406, #29651, #29659, #31889 and #32033 by @ClearlyClaire, @Gargron, @eai04191, @hinaloe, and @ronilaukkarinen)\ - **Change design of compose form in web UI** (#28119, #29059, #29248, #29372, #29384, #29417, #29456, #29406, #29651, #29659, #31889 and #32033 by @ClearlyClaire, @Gargron, @eai04191, @hinaloe, and @ronilaukkarinen)\
@ -192,9 +194,9 @@ The following changelog entries focus on changes visible to users, administrator
Administrators may need to update their setup accordingly. Administrators may need to update their setup accordingly.
- Change how content warnings and filters are displayed in web UI (#31365, and #31761 by @Gargron) - Change how content warnings and filters are displayed in web UI (#31365, and #31761 by @Gargron)
- Change preview card processing to ignore `undefined` as canonical url (#31882 by @oneiros) - Change preview card processing to ignore `undefined` as canonical url (#31882 by @oneiros)
- Change embedded posts to use web UI (#31766 and #32135 by @Gargron) - Change embedded posts to use web UI (#31766, #32135 and #32271 by @Gargron)
- Change inner borders in media galleries in web UI (#31852 by @Gargron) - Change inner borders in media galleries in web UI (#31852 by @Gargron)
- Change design of media attachments and profile media tab in web UI (#31807, #32048, and #31967 by @Gargron) - Change design of media attachments and profile media tab in web UI (#31807, #32048, #31967, #32217, #32224 and #32257 by @ClearlyClaire and @Gargron)
- Change labels on thread indicators in web UI (#31806 by @Gargron) - Change labels on thread indicators in web UI (#31806 by @Gargron)
- Change label of "Data export" menu item in settings interface (#32099 by @c960657) - Change label of "Data export" menu item in settings interface (#32099 by @c960657)
- Change responsive break points on navigation panel in web UI (#32034 by @Gargron) - Change responsive break points on navigation panel in web UI (#32034 by @Gargron)
@ -284,9 +286,10 @@ The following changelog entries focus on changes visible to users, administrator
- Fix error when accepting an appeal for sensitive posts deleted in the meantime (#32037 by @ClearlyClaire) - Fix error when accepting an appeal for sensitive posts deleted in the meantime (#32037 by @ClearlyClaire)
- Fix error when encountering reblog of deleted post in feed rebuild (#32001 by @ClearlyClaire) - Fix error when encountering reblog of deleted post in feed rebuild (#32001 by @ClearlyClaire)
- Fix Safari browser glitch related to horizontal scrolling (#31960 by @Gargron) - Fix Safari browser glitch related to horizontal scrolling (#31960 by @Gargron)
- Fix unresolvable mentions sometimes preventing processing incoming posts (#29215 by @tribela and @ClearlyClaire)
- Fix too many requests caused by relationship look-ups in web UI (#32042 by @Gargron) - Fix too many requests caused by relationship look-ups in web UI (#32042 by @Gargron)
- Fix links for reblogs in moderation interface (#31979 by @ClearlyClaire) - Fix links for reblogs in moderation interface (#31979 by @ClearlyClaire)
- Fix the appearance of avatars when they do not load (#31966 by @renchap) - Fix the appearance of avatars when they do not load (#31966 and #32270 by @Gargron and @renchap)
- Fix spurious error notifications for aborted requests in web UI (#31952 by @c960657) - Fix spurious error notifications for aborted requests in web UI (#31952 by @c960657)
- Fix HTTP 500 error in `/api/v1/polls/:id/votes` when required `choices` parameter is missing (#25598 by @danielmbrasil) - Fix HTTP 500 error in `/api/v1/polls/:id/votes` when required `choices` parameter is missing (#25598 by @danielmbrasil)
- Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire) - Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire)
@ -309,10 +312,12 @@ The following changelog entries focus on changes visible to users, administrator
- Fix “Redirect URI” field not being marked as required in “New application” form (#30311 by @ThisIsMissEm) - Fix “Redirect URI” field not being marked as required in “New application” form (#30311 by @ThisIsMissEm)
- Fix right-to-left text in preview cards (#30930 by @ClearlyClaire) - Fix right-to-left text in preview cards (#30930 by @ClearlyClaire)
- Fix rack attack `match_type` value typo in logging config (#30514 by @mjankowski) - Fix rack attack `match_type` value typo in logging config (#30514 by @mjankowski)
- Fix various cases of duplicate, missing, or inconsistent borders or scrollbar styles (#31068, #31286, #31268, #31275, #31284, #31305, #31346, #31372, #31373, #31389, #31432, #31391, #31445 and #32091 by @ClearlyClaire, @valtlai and @vmstan) - Fix various cases of duplicate, missing, or inconsistent borders or scrollbar styles (#31068, #31286, #31268, #31275, #31284, #31305, #31346, #31372, #31373, #31389, #31432, #31391, #31445, #32091, #32147 and #32137 by @ClearlyClaire, @mjankowski, @valtlai and @vmstan)
- Fix editing description of media uploads with custom thumbnails (#32221 by @ClearlyClaire)
- Fix race condition in `POST /api/v1/push/subscription` (#30166 by @ClearlyClaire) - Fix race condition in `POST /api/v1/push/subscription` (#30166 by @ClearlyClaire)
- Fix post deletion not being delayed when those are part of an account warning (#30163 by @ClearlyClaire) - Fix post deletion not being delayed when those are part of an account warning (#30163 by @ClearlyClaire)
- Fix rendering error on `/start` when not logged in (#30023 by @timothyjrogers) - Fix rendering error on `/start` when not logged in (#30023 by @timothyjrogers)
- Fix unneeded requests to blocked domains when receiving relayed signed activities from them (#31161 by @ClearlyClaire)
- Fix logo pushing header buttons out of view on certain conditions in mobile layout (#29787 by @ClearlyClaire) - Fix logo pushing header buttons out of view on certain conditions in mobile layout (#29787 by @ClearlyClaire)
- Fix notification-related records not being reattributed when merging accounts (#29694 by @ClearlyClaire) - Fix notification-related records not being reattributed when merging accounts (#29694 by @ClearlyClaire)
- Fix results/query in `api/v1/featured_tags/suggestions` (#29597 by @mjankowski) - Fix results/query in `api/v1/featured_tags/suggestions` (#29597 by @mjankowski)
@ -322,6 +327,7 @@ The following changelog entries focus on changes visible to users, administrator
- Fix full date display not respecting the locale 12/24h format (#29448 by @renchap) - Fix full date display not respecting the locale 12/24h format (#29448 by @renchap)
- Fix filters title and keywords overflow (#29396 by @GeopJr) - Fix filters title and keywords overflow (#29396 by @GeopJr)
- Fix incorrect date format in “Follows and followers” (#29390 by @JasonPunyon) - Fix incorrect date format in “Follows and followers” (#29390 by @JasonPunyon)
- Fix navigation item active highlight for some paths (#32159 by @mjankowski)
- Fix “Edit media” modal sizing and layout when space-constrained (#27095 by @ronilaukkarinen) - Fix “Edit media” modal sizing and layout when space-constrained (#27095 by @ronilaukkarinen)
- Fix modal container bounds (#29185 by @nico3333fr) - Fix modal container bounds (#29185 by @nico3333fr)
- Fix inefficient HTTP signature parsing using regexps and `StringScanner` (#29133 by @ClearlyClaire) - Fix inefficient HTTP signature parsing using regexps and `StringScanner` (#29133 by @ClearlyClaire)

View File

@ -615,7 +615,7 @@ GEM
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rack (2.2.9) rack (2.2.10)
rack-attack (6.7.0) rack-attack (6.7.0)
rack (>= 1.0, < 4) rack (>= 1.0, < 4)
rack-cors (2.0.2) rack-cors (2.0.2)

View File

@ -13,8 +13,9 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
## Supported Versions ## Supported Versions
| Version | Supported | | Version | Supported |
| ------- | --------- | | ------- | ---------------- |
| 4.2.x | Yes | | 4.3.x | Yes |
| 4.1.x | Yes | | 4.2.x | Yes |
| < 4.1 | No | | 4.1.x | Until 2025-04-08 |
| < 4.1 | No |

View File

@ -52,7 +52,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
private private
def load_requests def load_requests
requests = NotificationRequest.where(account: current_account).includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id( requests = NotificationRequest.where(account: current_account).without_suspended.includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT), limit_param(DEFAULT_ACCOUNTS_LIMIT),
params_slice(:max_id, :since_id, :min_id) params_slice(:max_id, :since_id, :min_id)
) )

View File

@ -35,7 +35,7 @@ class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
rescue_from Seahorse::Client::NetworkingError do |e| rescue_from Seahorse::Client::NetworkingError do |e|

View File

@ -20,7 +20,7 @@ module Api::ErrorHandling
render json: { error: 'Record not found' }, status: 404 render json: { error: 'Record not found' }, status: 404
end end
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::UnexpectedResponseError) do
render json: { error: 'Remote data could not be fetched' }, status: 503 render json: { error: 'Remote data could not be fetched' }, status: 503
end end

View File

@ -80,7 +80,7 @@ module SignatureVerification
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature'] fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
rescue SignatureVerificationError => e rescue SignatureVerificationError => e
fail_with! e.message fail_with! e.message
rescue HTTP::Error, OpenSSL::SSL::SSLError => e rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
fail_with! "Failed to fetch remote data: #{e.message}" fail_with! "Failed to fetch remote data: #{e.message}"
rescue Mastodon::UnexpectedResponseError rescue Mastodon::UnexpectedResponseError
fail_with! 'Failed to fetch remote data (got unexpected reply from server)' fail_with! 'Failed to fetch remote data (got unexpected reply from server)'

View File

@ -13,7 +13,7 @@ class MediaProxyController < ApplicationController
rescue_from ActiveRecord::RecordInvalid, with: :not_found rescue_from ActiveRecord::RecordInvalid, with: :not_found
rescue_from Mastodon::UnexpectedResponseError, with: :not_found rescue_from Mastodon::UnexpectedResponseError, with: :not_found
rescue_from Mastodon::NotPermittedError, with: :not_found rescue_from Mastodon::NotPermittedError, with: :not_found
rescue_from HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, with: :internal_server_error rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
def show def show
with_redis_lock("media_download:#{params[:id]}") do with_redis_lock("media_download:#{params[:id]}") do

View File

@ -193,6 +193,7 @@ module LanguagesHelper
ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze, ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze,
cnr: ['Montenegrin', 'crnogorski'].freeze, cnr: ['Montenegrin', 'crnogorski'].freeze,
csb: ['Kashubian', 'Kaszëbsczi'].freeze, csb: ['Kashubian', 'Kaszëbsczi'].freeze,
gsw: ['Swiss German', 'Schwiizertütsch'].freeze,
jbo: ['Lojban', 'la .lojban.'].freeze, jbo: ['Lojban', 'la .lojban.'].freeze,
kab: ['Kabyle', 'Taqbaylit'].freeze, kab: ['Kabyle', 'Taqbaylit'].freeze,
ldn: ['Láadan', 'Láadan'].freeze, ldn: ['Láadan', 'Láadan'].freeze,

View File

@ -13,7 +13,7 @@ export interface ApiAccountRoleJSON {
} }
// See app/serializers/rest/account_serializer.rb // See app/serializers/rest/account_serializer.rb
export interface ApiAccountJSON { export interface BaseApiAccountJSON {
acct: string; acct: string;
avatar: string; avatar: string;
avatar_static: string; avatar_static: string;
@ -45,3 +45,12 @@ export interface ApiAccountJSON {
memorial?: boolean; memorial?: boolean;
hide_collections: boolean; hide_collections: boolean;
} }
// See app/serializers/rest/muted_account_serializer.rb
export interface ApiMutedAccountJSON extends BaseApiAccountJSON {
mute_expires_at?: string | null;
}
// For now, we have the same type representing both `Account` and `MutedAccount`
// objects, but we should refactor this in the future.
export type ApiAccountJSON = ApiMutedAccountJSON;

View File

@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return; return;
} }
setCanScrollLeft(bodyRef.current.scrollLeft > 0); if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth); setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]); }, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
const handleLeftNav = useCallback(() => { const handleLeftNav = useCallback(() => {

View File

@ -16,6 +16,7 @@ import {
import type { IconProp } from 'flavours/glitch/components/icon'; import type { IconProp } from 'flavours/glitch/components/icon';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import Status from 'flavours/glitch/containers/status_container'; import Status from 'flavours/glitch/containers/status_container';
import { getStatusHidden } from 'flavours/glitch/selectors/filters';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
import { DisplayedName } from './displayed_name'; import { DisplayedName } from './displayed_name';
@ -51,6 +52,12 @@ export const NotificationWithStatus: React.FC<{
(state) => state.statuses.getIn([statusId, 'visibility']) === 'direct', (state) => state.statuses.getIn([statusId, 'visibility']) === 'direct',
); );
const isFiltered = useAppSelector(
(state) =>
statusId &&
getStatusHidden(state, { id: statusId, contextType: 'notifications' }),
);
const handlers = useMemo( const handlers = useMemo(
() => ({ () => ({
open: () => { open: () => {
@ -77,7 +84,7 @@ export const NotificationWithStatus: React.FC<{
[dispatch, statusId], [dispatch, statusId],
); );
if (!statusId) return null; if (!statusId || isFiltered) return null;
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>

View File

@ -15,6 +15,7 @@ import { Icon } from 'flavours/glitch/components/icon';
import { import {
selectSettingsNotificationsQuickFilterActive, selectSettingsNotificationsQuickFilterActive,
selectSettingsNotificationsQuickFilterAdvanced, selectSettingsNotificationsQuickFilterAdvanced,
selectSettingsNotificationsQuickFilterShow,
} from 'flavours/glitch/selectors/settings'; } from 'flavours/glitch/selectors/settings';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
@ -70,6 +71,11 @@ export const FilterBar: React.FC = () => {
const advancedMode = useAppSelector( const advancedMode = useAppSelector(
selectSettingsNotificationsQuickFilterAdvanced, selectSettingsNotificationsQuickFilterAdvanced,
); );
const useFilterBar = useAppSelector(
selectSettingsNotificationsQuickFilterShow,
);
if (!useFilterBar) return null;
if (advancedMode) if (advancedMode)
return ( return (

View File

@ -21,6 +21,7 @@ import { Permalink } from 'flavours/glitch/components/permalink';
import { PictureInPicture } from 'flavours/glitch/features/picture_in_picture'; import { PictureInPicture } from 'flavours/glitch/features/picture_in_picture';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { layoutFromWindow } from 'flavours/glitch/is_mobile'; import { layoutFromWindow } from 'flavours/glitch/is_mobile';
import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose'; import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
@ -90,7 +91,7 @@ const mapStateToProps = state => ({
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0, hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4, canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
isWide: state.getIn(['local_settings', 'stretch']), isWide: state.getIn(['local_settings', 'stretch']),
unreadNotifications: state.getIn(['notifications', 'unread']), unreadNotifications: selectUnreadNotificationGroupsCount(state),
showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']), showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']),
hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']), hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']),
moved: state.getIn(['accounts', me, 'moved']) && state.getIn(['accounts', state.getIn(['accounts', me, 'moved'])]), moved: state.getIn(['accounts', me, 'moved']) && state.getIn(['accounts', state.getIn(['accounts', me, 'moved'])]),

View File

@ -95,6 +95,9 @@ export const accountDefaultValues: AccountShape = {
limited: false, limited: false,
moved: null, moved: null,
hide_collections: false, hide_collections: false,
// This comes from `ApiMutedAccountJSON`, but we should eventually
// store that in a different object.
mute_expires_at: null,
}; };
const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues); const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);

View File

@ -559,7 +559,10 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0 compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0
) )
state.lastReadId = mostRecentGroup.page_max_id; state.lastReadId = mostRecentGroup.page_max_id;
commitLastReadId(state);
// We don't call `commitLastReadId`, because that is conditional
// and we want to unconditionally update the state instead.
state.readMarkerId = state.lastReadId;
}) })
.addCase(fetchMarkers.fulfilled, (state, action) => { .addCase(fetchMarkers.fulfilled, (state, action) => {
if ( if (

View File

@ -0,0 +1,50 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'flavours/glitch/store';
import { toServerSideType } from 'flavours/glitch/utils/filters';
// TODO: move to `app/javascript/flavours/glitch/models` and use more globally
type Filter = Immutable.Map<string, unknown>;
// TODO: move to `app/javascript/flavours/glitch/models` and use more globally
type FilterResult = Immutable.Map<string, unknown>;
export const getFilters = createSelector(
[
(state: RootState) => state.filters as Immutable.Map<string, Filter>,
(_, { contextType }: { contextType: string }) => contextType,
],
(filters, contextType) => {
if (!contextType) {
return null;
}
const now = new Date();
const serverSideType = toServerSideType(contextType);
return filters.filter((filter) => {
const context = filter.get('context') as Immutable.List<string>;
const expiration = filter.get('expires_at') as Date | null;
return (
context.includes(serverSideType) &&
(expiration === null || expiration > now)
);
});
},
);
export const getStatusHidden = (
state: RootState,
{ id, contextType }: { id: string; contextType: string },
) => {
const filters = getFilters(state, { contextType });
if (filters === null) return false;
const filtered = state.statuses.getIn([id, 'filtered']) as
| Immutable.List<FilterResult>
| undefined;
return filtered?.some(
(result) =>
filters.getIn([result.get('filter'), 'filter_action']) === 'hide',
);
};

View File

@ -1,23 +1,12 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { toServerSideType } from 'flavours/glitch/utils/filters';
import { me } from '../initial_state'; import { me } from '../initial_state';
import { getFilters } from './filters';
export { makeGetAccount } from "./accounts"; export { makeGetAccount } from "./accounts";
const getFilters = createSelector([state => state.get('filters'), (_, { contextType }) => contextType], (filters, contextType) => {
if (!contextType) {
return null;
}
const now = new Date();
const serverSideType = toServerSideType(contextType);
return filters.filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now));
});
export const makeGetStatus = () => { export const makeGetStatus = () => {
return createSelector( return createSelector(
[ [

View File

@ -1050,6 +1050,12 @@ a.name-tag,
color: var(--user-role-accent); color: var(--user-role-accent);
} }
.applications-list {
.icon {
vertical-align: middle;
}
}
.announcements-list, .announcements-list,
.filters-list { .filters-list {
border: 1px solid var(--background-border-color); border: 1px solid var(--background-border-color);

View File

@ -8542,79 +8542,23 @@ noscript {
background: rgba($base-overlay-background, 0.5); background: rgba($base-overlay-background, 0.5);
} }
.list-adder,
.list-editor { .list-editor {
background: $ui-base-color; backdrop-filter: var(--background-filter);
background: var(--modal-background-color);
border: 1px solid var(--modal-border-color);
flex-direction: column; flex-direction: column;
border-radius: 8px; border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px; width: 380px;
overflow: hidden; overflow: hidden;
@media screen and (width <= 420px) { @media screen and (width <= 420px) {
width: 90%; width: 90%;
} }
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border-radius: 4px;
}
.drawer__inner {
border-radius: 0 0 8px 8px;
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
}
} }
.list-adder { .list-adder {
background: $ui-base-color;
flex-direction: column;
border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px;
overflow: hidden;
@media screen and (width <= 420px) {
width: 90%;
}
&__account {
background: lighten($ui-base-color, 13%);
}
&__lists { &__lists {
background: lighten($ui-base-color, 13%);
height: 50vh; height: 50vh;
border-radius: 0 0 8px 8px; border-radius: 0 0 8px 8px;
overflow-y: auto; overflow-y: auto;
@ -8635,6 +8579,52 @@ noscript {
text-decoration: none; text-decoration: none;
font-size: 16px; font-size: 16px;
padding: 10px; padding: 10px;
display: flex;
align-items: center;
gap: 4px;
}
}
.list-editor {
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border: 0;
}
.drawer__inner {
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
background: unset;
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
} }
} }

View File

@ -13,7 +13,7 @@ export interface ApiAccountRoleJSON {
} }
// See app/serializers/rest/account_serializer.rb // See app/serializers/rest/account_serializer.rb
export interface ApiAccountJSON { export interface BaseApiAccountJSON {
acct: string; acct: string;
avatar: string; avatar: string;
avatar_static: string; avatar_static: string;
@ -45,3 +45,12 @@ export interface ApiAccountJSON {
memorial?: boolean; memorial?: boolean;
hide_collections: boolean; hide_collections: boolean;
} }
// See app/serializers/rest/muted_account_serializer.rb
export interface ApiMutedAccountJSON extends BaseApiAccountJSON {
mute_expires_at?: string | null;
}
// For now, we have the same type representing both `Account` and `MutedAccount`
// objects, but we should refactor this in the future.
export type ApiAccountJSON = ApiMutedAccountJSON;

View File

@ -129,8 +129,13 @@ export const InlineFollowSuggestions = ({ hidden }) => {
return; return;
} }
setCanScrollLeft(bodyRef.current.scrollLeft > 0); if (getComputedStyle(bodyRef.current).direction === 'rtl') {
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth); setCanScrollLeft((bodyRef.current.clientWidth - bodyRef.current.scrollLeft) < bodyRef.current.scrollWidth);
setCanScrollRight(bodyRef.current.scrollLeft < 0);
} else {
setCanScrollLeft(bodyRef.current.scrollLeft > 0);
setCanScrollRight((bodyRef.current.scrollLeft + bodyRef.current.clientWidth) < bodyRef.current.scrollWidth);
}
}, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]); }, [setCanScrollRight, setCanScrollLeft, bodyRef, suggestions]);
const handleLeftNav = useCallback(() => { const handleLeftNav = useCallback(() => {

View File

@ -13,6 +13,7 @@ import {
import type { IconProp } from 'mastodon/components/icon'; import type { IconProp } from 'mastodon/components/icon';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import Status from 'mastodon/containers/status_container'; import Status from 'mastodon/containers/status_container';
import { getStatusHidden } from 'mastodon/selectors/filters';
import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { DisplayedName } from './displayed_name'; import { DisplayedName } from './displayed_name';
@ -48,6 +49,12 @@ export const NotificationWithStatus: React.FC<{
(state) => state.statuses.getIn([statusId, 'visibility']) === 'direct', (state) => state.statuses.getIn([statusId, 'visibility']) === 'direct',
); );
const isFiltered = useAppSelector(
(state) =>
statusId &&
getStatusHidden(state, { id: statusId, contextType: 'notifications' }),
);
const handlers = useMemo( const handlers = useMemo(
() => ({ () => ({
open: () => { open: () => {
@ -73,7 +80,7 @@ export const NotificationWithStatus: React.FC<{
[dispatch, statusId], [dispatch, statusId],
); );
if (!statusId) return null; if (!statusId || isFiltered) return null;
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>

View File

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Ліміт перавышаны", "alert.rate_limited.title": "Ліміт перавышаны",
"alert.unexpected.message": "Узнікла нечаканая памылка.", "alert.unexpected.message": "Узнікла нечаканая памылка.",
"alert.unexpected.title": "Вой!", "alert.unexpected.title": "Вой!",
"alt_text_badge.title": "Альтернативный текст",
"announcement.announcement": "Аб'ява", "announcement.announcement": "Аб'ява",
"attachments_list.unprocessed": "(неапрацаваны)", "attachments_list.unprocessed": "(неапрацаваны)",
"audio.hide": "Схаваць аўдыя", "audio.hide": "Схаваць аўдыя",

View File

@ -62,7 +62,7 @@
"account.requested_follow": "{name} möchte dir folgen", "account.requested_follow": "{name} möchte dir folgen",
"account.share": "Profil von @{name} teilen", "account.share": "Profil von @{name} teilen",
"account.show_reblogs": "Geteilte Beiträge von @{name} anzeigen", "account.show_reblogs": "Geteilte Beiträge von @{name} anzeigen",
"account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} posts}}", "account.statuses_counter": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}",
"account.unblock": "Blockierung von @{name} aufheben", "account.unblock": "Blockierung von @{name} aufheben",
"account.unblock_domain": "Blockierung von {domain} aufheben", "account.unblock_domain": "Blockierung von {domain} aufheben",
"account.unblock_short": "Blockierung aufheben", "account.unblock_short": "Blockierung aufheben",

View File

@ -95,6 +95,9 @@ export const accountDefaultValues: AccountShape = {
limited: false, limited: false,
moved: null, moved: null,
hide_collections: false, hide_collections: false,
// This comes from `ApiMutedAccountJSON`, but we should eventually
// store that in a different object.
mute_expires_at: null,
}; };
const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues); const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);

View File

@ -559,7 +559,10 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0 compareId(state.lastReadId, mostRecentGroup.page_max_id) < 0
) )
state.lastReadId = mostRecentGroup.page_max_id; state.lastReadId = mostRecentGroup.page_max_id;
commitLastReadId(state);
// We don't call `commitLastReadId`, because that is conditional
// and we want to unconditionally update the state instead.
state.readMarkerId = state.lastReadId;
}) })
.addCase(fetchMarkers.fulfilled, (state, action) => { .addCase(fetchMarkers.fulfilled, (state, action) => {
if ( if (

View File

@ -0,0 +1,50 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'mastodon/store';
import { toServerSideType } from 'mastodon/utils/filters';
// TODO: move to `app/javascript/mastodon/models` and use more globally
type Filter = Immutable.Map<string, unknown>;
// TODO: move to `app/javascript/mastodon/models` and use more globally
type FilterResult = Immutable.Map<string, unknown>;
export const getFilters = createSelector(
[
(state: RootState) => state.filters as Immutable.Map<string, Filter>,
(_, { contextType }: { contextType: string }) => contextType,
],
(filters, contextType) => {
if (!contextType) {
return null;
}
const now = new Date();
const serverSideType = toServerSideType(contextType);
return filters.filter((filter) => {
const context = filter.get('context') as Immutable.List<string>;
const expiration = filter.get('expires_at') as Date | null;
return (
context.includes(serverSideType) &&
(expiration === null || expiration > now)
);
});
},
);
export const getStatusHidden = (
state: RootState,
{ id, contextType }: { id: string; contextType: string },
) => {
const filters = getFilters(state, { contextType });
if (filters === null) return false;
const filtered = state.statuses.getIn([id, 'filtered']) as
| Immutable.List<FilterResult>
| undefined;
return filtered?.some(
(result) =>
filters.getIn([result.get('filter'), 'filter_action']) === 'hide',
);
};

View File

@ -1,23 +1,12 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { toServerSideType } from 'mastodon/utils/filters';
import { me } from '../initial_state'; import { me } from '../initial_state';
import { getFilters } from './filters';
export { makeGetAccount } from "./accounts"; export { makeGetAccount } from "./accounts";
const getFilters = createSelector([state => state.get('filters'), (_, { contextType }) => contextType], (filters, contextType) => {
if (!contextType) {
return null;
}
const now = new Date();
const serverSideType = toServerSideType(contextType);
return filters.filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now));
});
export const makeGetStatus = () => { export const makeGetStatus = () => {
return createSelector( return createSelector(
[ [

View File

@ -1045,6 +1045,12 @@ a.name-tag,
color: var(--user-role-accent); color: var(--user-role-accent);
} }
.applications-list {
.icon {
vertical-align: middle;
}
}
.announcements-list, .announcements-list,
.filters-list { .filters-list {
border: 1px solid var(--background-border-color); border: 1px solid var(--background-border-color);

View File

@ -2878,7 +2878,7 @@ $ui-header-logo-wordmark-width: 99px;
} }
.column { .column {
width: 400px; width: clamp(380px, calc((100% - 350px) / 4), 400px);
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
@ -8007,79 +8007,23 @@ noscript {
background: rgba($base-overlay-background, 0.5); background: rgba($base-overlay-background, 0.5);
} }
.list-adder,
.list-editor { .list-editor {
background: $ui-base-color; backdrop-filter: var(--background-filter);
background: var(--modal-background-color);
border: 1px solid var(--modal-border-color);
flex-direction: column; flex-direction: column;
border-radius: 8px; border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px; width: 380px;
overflow: hidden; overflow: hidden;
@media screen and (width <= 420px) { @media screen and (width <= 420px) {
width: 90%; width: 90%;
} }
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border-radius: 4px;
}
.drawer__inner {
border-radius: 0 0 8px 8px;
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
}
} }
.list-adder { .list-adder {
background: $ui-base-color;
flex-direction: column;
border-radius: 8px;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
width: 380px;
overflow: hidden;
@media screen and (width <= 420px) {
width: 90%;
}
&__account {
background: lighten($ui-base-color, 13%);
}
&__lists { &__lists {
background: lighten($ui-base-color, 13%);
height: 50vh; height: 50vh;
border-radius: 0 0 8px 8px; border-radius: 0 0 8px 8px;
overflow-y: auto; overflow-y: auto;
@ -8100,6 +8044,52 @@ noscript {
text-decoration: none; text-decoration: none;
font-size: 16px; font-size: 16px;
padding: 10px; padding: 10px;
display: flex;
align-items: center;
gap: 4px;
}
}
.list-editor {
h4 {
padding: 15px 0;
background: lighten($ui-base-color, 13%);
font-weight: 500;
font-size: 16px;
text-align: center;
border-radius: 8px 8px 0 0;
}
.drawer__pager {
height: 50vh;
border: 0;
}
.drawer__inner {
&.backdrop {
width: calc(100% - 60px);
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 0 0 0 8px;
}
}
&__accounts {
background: unset;
overflow-y: auto;
}
.account__display-name {
&:hover strong {
text-decoration: none;
}
}
.account__avatar {
cursor: default;
}
.search {
margin-bottom: 0;
} }
} }

View File

@ -199,7 +199,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
return if account.nil? return if account.nil?
@mentions << Mention.new(account: account, silent: false) @mentions << Mention.new(account: account, silent: false)
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
@unresolved_mentions << tag['href'] @unresolved_mentions << tag['href']
end end
@ -250,7 +250,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
media_attachment.download_file! media_attachment.download_file!
media_attachment.download_thumbnail! media_attachment.download_thumbnail!
media_attachment.save media_attachment.save
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id) RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id)
rescue Seahorse::Client::NetworkingError => e rescue Seahorse::Client::NetworkingError => e
Rails.logger.warn "Error storing media attachment: #{e}" Rails.logger.warn "Error storing media attachment: #{e}"

View File

@ -35,7 +35,7 @@ class AccountAlias < ApplicationRecord
def set_uri def set_uri
target_account = ResolveAccountService.new.call(acct) target_account = ResolveAccountService.new.call(acct)
self.uri = ActivityPub::TagManager.instance.uri_for(target_account) unless target_account.nil? self.uri = ActivityPub::TagManager.instance.uri_for(target_account) unless target_account.nil?
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error rescue Webfinger::Error, *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::Error
# Validation will take care of it # Validation will take care of it
end end

View File

@ -61,7 +61,7 @@ class AccountMigration < ApplicationRecord
def set_target_account def set_target_account
self.target_account = ResolveAccountService.new.call(acct, skip_cache: true) self.target_account = ResolveAccountService.new.call(acct, skip_cache: true)
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError rescue Webfinger::Error, *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::Error, Addressable::URI::InvalidURIError
# Validation will take care of it # Validation will take care of it
end end

View File

@ -26,7 +26,7 @@ module Remotable
public_send(:"#{attachment_name}=", ResponseWithLimit.new(response, limit)) public_send(:"#{attachment_name}=", ResponseWithLimit.new(response, limit))
end end
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS => e
Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" } Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" }
public_send(:"#{attachment_name}=", nil) if public_send(:"#{attachment_name}_file_name").present? public_send(:"#{attachment_name}=", nil) if public_send(:"#{attachment_name}_file_name").present?
raise e unless suppress_errors raise e unless suppress_errors

View File

@ -40,7 +40,9 @@ class DomainBlock < ApplicationRecord
if suspend? if suspend?
[:suspend] [:suspend]
else else
[severity.to_sym, reject_media? ? :reject_media : nil, reject_reports? ? :reject_reports : nil].reject { |policy| policy == :noop || policy.nil? } [severity.to_sym, reject_media? ? :reject_media : nil, reject_reports? ? :reject_reports : nil]
.reject { |policy| policy == :noop }
.compact
end end
end end

View File

@ -18,5 +18,6 @@ class FollowRecommendation < ApplicationRecord
belongs_to :account_summary, foreign_key: :account_id, inverse_of: false belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
belongs_to :account belongs_to :account
scope :localized, ->(locale) { joins(:account_summary).merge(AccountSummary.localized(locale)) } scope :unsupressed, -> { where.not(FollowRecommendationSuppression.where(FollowRecommendationSuppression.arel_table[:account_id].eq(arel_table[:account_id])).select(1).arel.exists) }
scope :localized, ->(locale) { unsupressed.joins(:account_summary).merge(AccountSummary.localized(locale)) }
end end

View File

@ -32,7 +32,7 @@ class Form::Redirect
def set_target_account def set_target_account
@target_account = ResolveAccountService.new.call(acct, skip_cache: true) @target_account = ResolveAccountService.new.call(acct, skip_cache: true)
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError rescue Webfinger::Error, *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::Error, Addressable::URI::InvalidURIError
# Validation will take care of it # Validation will take care of it
end end

View File

@ -62,6 +62,6 @@ class NotificationPolicy < ApplicationRecord
private private
def pending_notification_requests def pending_notification_requests
@pending_notification_requests ||= notification_requests.limit(MAX_MEANINGFUL_COUNT).pick(Arel.sql('count(*), coalesce(sum(notifications_count), 0)::bigint')) @pending_notification_requests ||= notification_requests.without_suspended.limit(MAX_MEANINGFUL_COUNT).pick(Arel.sql('count(*), coalesce(sum(notifications_count), 0)::bigint'))
end end
end end

View File

@ -26,6 +26,8 @@ class NotificationRequest < ApplicationRecord
before_save :prepare_notifications_count before_save :prepare_notifications_count
scope :without_suspended, -> { joins(:from_account).merge(Account.without_suspended) }
def self.preload_cache_collection(requests) def self.preload_cache_collection(requests)
cached_statuses_by_id = yield(requests.filter_map(&:last_status)).index_by(&:id) # Call cache_collection in block cached_statuses_by_id = yield(requests.filter_map(&:last_status)).index_by(&:id) # Call cache_collection in block

View File

@ -66,7 +66,7 @@ class RemoteFollow
def acct_resource def acct_resource
@acct_resource ||= Webfinger.new("acct:#{acct}").perform @acct_resource ||= Webfinger.new("acct:#{acct}").perform
rescue Webfinger::Error, HTTP::ConnectionError rescue Webfinger::Error, *Mastodon::HTTP_CONNECTION_ERRORS
nil nil
end end

View File

@ -8,7 +8,8 @@ class ManifestSerializer < ActiveModel::Serializer
attributes :id, :name, :short_name, attributes :id, :name, :short_name,
:icons, :theme_color, :background_color, :icons, :theme_color, :background_color,
:display, :start_url, :scope, :display, :start_url, :scope,
:share_target, :shortcuts :share_target, :shortcuts,
:prefer_related_applications, :related_applications
def id def id
# This is set to `/home` because that was the old value of `start_url` and # This is set to `/home` because that was the old value of `start_url` and
@ -89,4 +90,28 @@ class ManifestSerializer < ActiveModel::Serializer
}, },
] ]
end end
def prefer_related_applications
true
end
def related_applications
[
{
platform: 'play',
url: 'https://play.google.com/store/apps/details?id=org.joinmastodon.android',
id: 'org.joinmastodon.android',
},
{
platform: 'itunes',
url: 'https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974',
id: 'id1571998974',
},
{
platform: 'f-droid',
url: 'https://f-droid.org/en/packages/org.joinmastodon.android/',
id: 'org.joinmastodon.android',
},
]
end
end end

View File

@ -127,13 +127,13 @@ class ActivityPub::ProcessAccountService < BaseService
begin begin
@account.avatar_remote_url = image_url('icon') || '' unless skip_download? @account.avatar_remote_url = image_url('icon') || '' unless skip_download?
@account.avatar = nil if @account.avatar_remote_url.blank? @account.avatar = nil if @account.avatar_remote_url.blank?
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
RedownloadAvatarWorker.perform_in(rand(30..600).seconds, @account.id) RedownloadAvatarWorker.perform_in(rand(30..600).seconds, @account.id)
end end
begin begin
@account.header_remote_url = image_url('image') || '' unless skip_download? @account.header_remote_url = image_url('image') || '' unless skip_download?
@account.header = nil if @account.header_remote_url.blank? @account.header = nil if @account.header_remote_url.blank?
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
RedownloadHeaderWorker.perform_in(rand(30..600).seconds, @account.id) RedownloadHeaderWorker.perform_in(rand(30..600).seconds, @account.id)
end end
@account.statuses_count = outbox_total_items if outbox_total_items.present? @account.statuses_count = outbox_total_items if outbox_total_items.present?
@ -276,7 +276,7 @@ class ActivityPub::ProcessAccountService < BaseService
total_items = collection.is_a?(Hash) && collection['totalItems'].present? && collection['totalItems'].is_a?(Numeric) ? collection['totalItems'] : nil total_items = collection.is_a?(Hash) && collection['totalItems'].present? && collection['totalItems'].is_a?(Numeric) ? collection['totalItems'] : nil
has_first_page = collection.is_a?(Hash) && collection['first'].present? has_first_page = collection.is_a?(Hash) && collection['first'].present?
@collections[type] = [total_items, has_first_page] @collections[type] = [total_items, has_first_page]
rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::LengthValidationError rescue *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::LengthValidationError
@collections[type] = [nil, nil] @collections[type] = [nil, nil]
end end

View File

@ -109,7 +109,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
media_attachment.download_file! if media_attachment.remote_url_previously_changed? media_attachment.download_file! if media_attachment.remote_url_previously_changed?
media_attachment.download_thumbnail! if media_attachment.thumbnail_remote_url_previously_changed? media_attachment.download_thumbnail! if media_attachment.thumbnail_remote_url_previously_changed?
media_attachment.save media_attachment.save
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id) RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id)
rescue Seahorse::Client::NetworkingError => e rescue Seahorse::Client::NetworkingError => e
Rails.logger.warn "Error storing media attachment: #{e}" Rails.logger.warn "Error storing media attachment: #{e}"

View File

@ -29,7 +29,7 @@ class FetchLinkCardService < BaseService
end end
attach_card if @card&.persisted? attach_card if @card&.persisted?
rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Encoding::UndefinedConversionError, ActiveRecord::RecordInvalid => e rescue *Mastodon::HTTP_CONNECTION_ERRORS, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Encoding::UndefinedConversionError, ActiveRecord::RecordInvalid => e
Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" } Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" }
nil nil
end end

View File

@ -12,7 +12,7 @@ class FetchResourceService < BaseService
return if url.blank? return if url.blank?
process(url) process(url)
rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e rescue *Mastodon::HTTP_CONNECTION_ERRORS, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
Rails.logger.debug { "Error fetching resource #{@url}: #{e}" } Rails.logger.debug { "Error fetching resource #{@url}: #{e}" }
nil nil
end end

View File

@ -115,7 +115,7 @@ class ImportService < BaseService
next if status.nil? && ActivityPub::TagManager.instance.local_uri?(uri) next if status.nil? && ActivityPub::TagManager.instance.local_uri?(uri)
status || ActivityPub::FetchRemoteStatusService.new.call(uri) status || ActivityPub::FetchRemoteStatusService.new.call(uri)
rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError rescue *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::UnexpectedResponseError
nil nil
rescue => e rescue => e
Rails.logger.warn "Unexpected error when importing bookmark: #{e}" Rails.logger.warn "Unexpected error when importing bookmark: #{e}"

View File

@ -44,7 +44,7 @@ class ProcessMentionsService < BaseService
if mention_undeliverable?(mentioned_account) if mention_undeliverable?(mentioned_account)
begin begin
mentioned_account = ResolveAccountService.new.call(Regexp.last_match(1)) mentioned_account = ResolveAccountService.new.call(Regexp.last_match(1))
rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError rescue Webfinger::Error, *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::UnexpectedResponseError
mentioned_account = nil mentioned_account = nil
end end
end end

View File

@ -22,7 +22,7 @@ class SoftwareUpdateCheckService < BaseService
Request.new(:get, "#{api_url}?version=#{version}").add_headers('Accept' => 'application/json', 'User-Agent' => 'Mastodon update checker').perform do |res| Request.new(:get, "#{api_url}?version=#{version}").add_headers('Accept' => 'application/json', 'User-Agent' => 'Mastodon update checker').perform do |res|
return Oj.load(res.body_with_limit, mode: :strict) if res.code == 200 return Oj.load(res.body_with_limit, mode: :strict) if res.code == 200
end end
rescue HTTP::Error, OpenSSL::SSL::SSLError, Oj::ParseError rescue *Mastodon::HTTP_CONNECTION_ERRORS, Oj::ParseError
nil nil
end end

View File

@ -10,7 +10,7 @@ class VerifyLinkService < BaseService
return unless link_back_present? return unless link_back_present?
field.mark_verified! field.mark_verified!
rescue OpenSSL::SSL::SSLError, HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, IPAddr::AddressFamilyError => e rescue *Mastodon::HTTP_CONNECTION_ERRORS, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, IPAddr::AddressFamilyError => e
Rails.logger.debug { "Error fetching link #{@url}: #{e}" } Rails.logger.debug { "Error fetching link #{@url}: #{e}" }
nil nil
end end

View File

@ -21,7 +21,7 @@ class RefollowWorker
# Schedule re-follow # Schedule re-follow
begin begin
FollowService.new.call(follower, target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_limit: true) FollowService.new.call(follower, target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_limit: true)
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
next next
end end
end end

View File

@ -55,12 +55,8 @@ class Web::PushNotificationWorker
end end
def push_notification_json def push_notification_json
Oj.dump(serialized_notification_in_subscription_locale.as_json)
end
def serialized_notification_in_subscription_locale
I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do
serialized_notification Oj.dump(serialized_notification.as_json)
end end
end end

View File

@ -96,10 +96,6 @@ module Mastodon
config.middleware.use Rack::Attack config.middleware.use Rack::Attack
config.middleware.use Mastodon::RackMiddleware config.middleware.use Mastodon::RackMiddleware
initializer :deprecator do |app|
app.deprecators[:mastodon] = ActiveSupport::Deprecation.new('4.3', 'mastodon/mastodon')
end
config.before_configuration do config.before_configuration do
require 'mastodon/redis_configuration' require 'mastodon/redis_configuration'
::REDIS_CONFIGURATION = Mastodon::RedisConfiguration.new ::REDIS_CONFIGURATION = Mastodon::RedisConfiguration.new

View File

@ -1,56 +0,0 @@
# frozen_string_literal: true
# Some migrations have been present in glitch-soc for a long time and have then
# been merged in upstream Mastodon, under a different version number.
#
# This puts us in an uneasy situation in which if we remove upstream's
# migration file, people migrating from upstream will end up having a conflict
# with their already-ran migration.
#
# On the other hand, if we keep upstream's migration and remove our own,
# any current glitch-soc user will have a conflict during migration.
#
# For lack of a better solution, as those migrations are indeed identical,
# we decided monkey-patching Rails' Migrator to completely ignore the duplicate,
# keeping only the one that has run, or an arbitrary one.
ALLOWED_DUPLICATES = [2018_04_10_220657, 2018_08_31_171112].freeze
module ActiveRecord
class Migrator
def self.new(direction, migrations, schema_migration, internal_metadata, target_version = nil)
migrated = Set.new(Base.connection.migration_context.get_all_versions)
migrations.group_by(&:name).each_value do |duplicates|
next unless duplicates.length > 1 && duplicates.all? { |m| ALLOWED_DUPLICATES.include?(m.version) }
# We have a set of allowed duplicates. Keep the migrated one, if any.
non_migrated = duplicates.reject { |m| migrated.include?(m.version.to_i) }
migrations = begin
if duplicates.length == non_migrated.length || non_migrated.empty?
# There weren't any migrated one, so we have to pick one “canonical” migration
migrations - duplicates[1..]
else
# Just reject every duplicate which hasn't been migrated yet
migrations - non_migrated
end
end
end
super
end
end
class MigrationContext
def needs_migration?
# A set of duplicated migrations is considered migrated if at least one of
# them is migrated.
migrated = get_all_versions
migrations.group_by(&:name).each_value do |duplicates|
return true unless duplicates.any? { |m| migrated.include?(m.version.to_i) }
end
false
end
end
end

View File

@ -20,6 +20,7 @@
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
Run `bin/rails db:encryption:init` to generate new secrets and then assign the environment variables. Run `bin/rails db:encryption:init` to generate new secrets and then assign the environment variables.
Do not change the secrets once they are set, as doing so may cause data loss and other issues that will be difficult or impossible to recover from.
MESSAGE MESSAGE
end end

View File

@ -21,6 +21,7 @@ ca:
one: Tut one: Tut
other: Tuts other: Tuts
posts_tab_heading: Tuts posts_tab_heading: Tuts
self_follow_error: No es permet seguir el compte propi
admin: admin:
account_actions: account_actions:
action: Realitza l'acció action: Realitza l'acció

View File

@ -21,6 +21,7 @@ da:
one: Indlæg one: Indlæg
other: Indlæg other: Indlæg
posts_tab_heading: Indlæg posts_tab_heading: Indlæg
self_follow_error: Det er ikke tilladt at følge sin egen konto
admin: admin:
account_actions: account_actions:
action: Udfør handling action: Udfør handling

View File

@ -21,6 +21,7 @@ de:
one: Beitrag one: Beitrag
other: Beiträge other: Beiträge
posts_tab_heading: Beiträge posts_tab_heading: Beiträge
self_follow_error: Es ist nicht erlaubt, deinem eigenen Konto zu folgen
admin: admin:
account_actions: account_actions:
action: Aktion ausführen action: Aktion ausführen

View File

@ -21,6 +21,7 @@ eo:
one: Afiŝo one: Afiŝo
other: Mesaĝoj other: Mesaĝoj
posts_tab_heading: Afiŝoj posts_tab_heading: Afiŝoj
self_follow_error: Sekvi vian propran konton ne estas permesita
admin: admin:
account_actions: account_actions:
action: Plenumi agon action: Plenumi agon

View File

@ -21,6 +21,7 @@ es-AR:
one: Mensaje one: Mensaje
other: Mensajes other: Mensajes
posts_tab_heading: Mensajes posts_tab_heading: Mensajes
self_follow_error: No está permitido seguir tu propia cuenta
admin: admin:
account_actions: account_actions:
action: Ejecutar acción action: Ejecutar acción

View File

@ -21,6 +21,7 @@ es-MX:
one: Toot one: Toot
other: Toots other: Toots
posts_tab_heading: Toots posts_tab_heading: Toots
self_follow_error: No se permite seguir tu propia cuenta
admin: admin:
account_actions: account_actions:
action: Realizar acción action: Realizar acción

View File

@ -21,6 +21,7 @@ es:
one: Publicación one: Publicación
other: Publicaciones other: Publicaciones
posts_tab_heading: Publicaciones posts_tab_heading: Publicaciones
self_follow_error: No está permitido seguir tu propia cuenta
admin: admin:
account_actions: account_actions:
action: Realizar acción action: Realizar acción

View File

@ -21,6 +21,7 @@ fi:
one: Julkaisu one: Julkaisu
other: viestiä other: viestiä
posts_tab_heading: Julkaisut posts_tab_heading: Julkaisut
self_follow_error: Oman tilisi seuraaminen ei ole sallittua
admin: admin:
account_actions: account_actions:
action: Suorita toimi action: Suorita toimi

View File

@ -21,6 +21,7 @@ fo:
one: Uppslag one: Uppslag
other: Uppsløg other: Uppsløg
posts_tab_heading: Uppsløg posts_tab_heading: Uppsløg
self_follow_error: Tað er ikki loyvt at fylgja tíni egnu kontu
admin: admin:
account_actions: account_actions:
action: Frem atgerð action: Frem atgerð

View File

@ -21,6 +21,7 @@ gl:
one: Publicación one: Publicación
other: Publicacións other: Publicacións
posts_tab_heading: Publicacións posts_tab_heading: Publicacións
self_follow_error: Non está permitido seguir a túa propia conta
admin: admin:
account_actions: account_actions:
action: Executar acción action: Executar acción

View File

@ -25,6 +25,7 @@ he:
other: הודעות other: הודעות
two: הודעותיים two: הודעותיים
posts_tab_heading: הודעות posts_tab_heading: הודעות
self_follow_error: בלתי אפשרי לך לעקוב אחרי חשבונך
admin: admin:
account_actions: account_actions:
action: בצע/י פעולה action: בצע/י פעולה

View File

@ -21,6 +21,7 @@ is:
one: Færsla one: Færsla
other: Færslur other: Færslur
posts_tab_heading: Færslur posts_tab_heading: Færslur
self_follow_error: Ekki er leyft að fylgjast með eigin aðgangi
admin: admin:
account_actions: account_actions:
action: Framkvæma aðgerð action: Framkvæma aðgerð

View File

@ -19,6 +19,7 @@ ja:
posts: posts:
other: 投稿 other: 投稿
posts_tab_heading: 投稿 posts_tab_heading: 投稿
self_follow_error: 自分のアカウントをフォローすることはできません
admin: admin:
account_actions: account_actions:
action: アクションを実行 action: アクションを実行

View File

@ -19,6 +19,7 @@ ko:
posts: posts:
other: 게시물 other: 게시물
posts_tab_heading: 게시물 posts_tab_heading: 게시물
self_follow_error: 본인의 계정을 팔로우할 수는 없습니다
admin: admin:
account_actions: account_actions:
action: 조치 취하기 action: 조치 취하기

View File

@ -21,6 +21,7 @@ nl:
one: Toot one: Toot
other: Berichten other: Berichten
posts_tab_heading: Berichten posts_tab_heading: Berichten
self_follow_error: Het volgen van je eigen account is niet toegestaan
admin: admin:
account_actions: account_actions:
action: Actie uitvoeren action: Actie uitvoeren

View File

@ -21,6 +21,7 @@ nn:
one: Tut one: Tut
other: Tut other: Tut
posts_tab_heading: Tut posts_tab_heading: Tut
self_follow_error: Det er ikkje tillate å følgje din eigen konto
admin: admin:
account_actions: account_actions:
action: Utfør action: Utfør

View File

@ -25,6 +25,7 @@ pl:
one: wpis one: wpis
other: Wpisów other: Wpisów
posts_tab_heading: Wpisy posts_tab_heading: Wpisy
self_follow_error: Nie możesz obserwować swojego konta
admin: admin:
account_actions: account_actions:
action: Wykonaj działanie action: Wykonaj działanie

View File

@ -21,6 +21,7 @@ pt-BR:
one: Publicação one: Publicação
other: Publicações other: Publicações
posts_tab_heading: Publicações posts_tab_heading: Publicações
self_follow_error: Seguir sua conta não é permitido
admin: admin:
account_actions: account_actions:
action: Tomar uma atitude action: Tomar uma atitude

View File

@ -21,6 +21,7 @@ tr:
one: Gönderi one: Gönderi
other: Gönderiler other: Gönderiler
posts_tab_heading: Gönderiler posts_tab_heading: Gönderiler
self_follow_error: Kendi hesabınızı takip etmenize izin yok
admin: admin:
account_actions: account_actions:
action: Eylemi gerçekleştir action: Eylemi gerçekleştir

View File

@ -25,6 +25,7 @@ uk:
one: Допис one: Допис
other: Дописів other: Дописів
posts_tab_heading: Дописів posts_tab_heading: Дописів
self_follow_error: Ви не можете стежити за власним обліковим записом
admin: admin:
account_actions: account_actions:
action: Виконати дію action: Виконати дію

View File

@ -19,6 +19,7 @@ vi:
posts: posts:
other: Tút other: Tút
posts_tab_heading: Tút posts_tab_heading: Tút
self_follow_error: Bạn không thể tự theo dõi chính bạn
admin: admin:
account_actions: account_actions:
action: Thực hiện hành động action: Thực hiện hành động

View File

@ -19,6 +19,7 @@ zh-CN:
posts: posts:
other: 嘟文 other: 嘟文
posts_tab_heading: 嘟文 posts_tab_heading: 嘟文
self_follow_error: 不可以关注自己
admin: admin:
account_actions: account_actions:
action: 执行操作 action: 执行操作

View File

@ -19,6 +19,7 @@ zh-TW:
posts: posts:
other: 嘟文 other: 嘟文
posts_tab_heading: 嘟文 posts_tab_heading: 嘟文
self_follow_error: 無法跟隨您自己的帳號
admin: admin:
account_actions: account_actions:
action: 執行動作 action: 執行動作

View File

@ -1,10 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
# This migration is a duplicate of 20180831171112 and may get ignored, see # This migration is a duplicate of 20180831171112
# config/initializers/0_duplicate_migrations.rb
class GlitchCreateBookmarks < ActiveRecord::Migration[5.1]
def up
return if table_exists?(:bookmarks)
class CreateBookmarks < ActiveRecord::Migration[5.1]
def change
create_table :bookmarks do |t| create_table :bookmarks do |t|
t.references :account, null: false t.references :account, null: false
t.references :status, null: false t.references :status, null: false
@ -19,4 +20,6 @@ class CreateBookmarks < ActiveRecord::Migration[5.1]
add_index :bookmarks, [:account_id, :status_id], unique: true add_index :bookmarks, [:account_id, :status_id], unique: true
end end
def down; end
end end

View File

@ -1,10 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
# This migration is a duplicate of 20180410220657 and may get ignored, see # This migration is a duplicate of 20180410220657
# config/initializers/0_duplicate_migrations.rb
class CreateBookmarks < ActiveRecord::Migration[5.2] class CreateBookmarks < ActiveRecord::Migration[5.2]
def change def up
return if table_exists?(:bookmarks)
create_table :bookmarks do |t| create_table :bookmarks do |t|
t.references :account, null: false t.references :account, null: false
t.references :status, null: false t.references :status, null: false
@ -19,4 +20,8 @@ class CreateBookmarks < ActiveRecord::Migration[5.2]
add_index :bookmarks, [:account_id, :status_id], unique: true add_index :bookmarks, [:account_id, :status_id], unique: true
end end
def down
drop_table :bookmarks
end
end end

View File

@ -59,7 +59,7 @@ services:
web: web:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
# build: . # build: .
image: ghcr.io/mastodon/mastodon:v4.3.0-rc.1 image: ghcr.io/mastodon/mastodon:v4.3.0
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec puma -C config/puma.rb command: bundle exec puma -C config/puma.rb
@ -83,7 +83,7 @@ services:
# build: # build:
# dockerfile: ./streaming/Dockerfile # dockerfile: ./streaming/Dockerfile
# context: . # context: .
image: ghcr.io/mastodon/mastodon-streaming:v4.3.0-rc.1 image: ghcr.io/mastodon/mastodon-streaming:v4.3.0
restart: always restart: always
env_file: .env.production env_file: .env.production
command: node ./streaming/index.js command: node ./streaming/index.js
@ -101,7 +101,7 @@ services:
sidekiq: sidekiq:
build: . build: .
image: ghcr.io/mastodon/mastodon:v4.3.0-rc.1 image: ghcr.io/mastodon/mastodon:v4.3.0
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec sidekiq command: bundle exec sidekiq

View File

@ -36,4 +36,11 @@ module Mastodon
super() super()
end end
end end
HTTP_CONNECTION_ERRORS = [
HTTP::ConnectionError,
HTTP::Error,
HTTP::TimeoutError,
OpenSSL::SSL::SSLError,
].freeze
end end

View File

@ -305,7 +305,7 @@ module Mastodon::CLI
begin begin
code = Request.new(:head, account.uri).perform(&:code) code = Request.new(:head, account.uri).perform(&:code)
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Mastodon::PrivateNetworkAddressError rescue *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::PrivateNetworkAddressError
skip_domains << account.domain skip_domains << account.domain
end end

View File

@ -5,7 +5,7 @@ require_relative 'base'
module Mastodon::CLI module Mastodon::CLI
class IpBlocks < Base class IpBlocks < Base
option :severity, required: true, enum: %w(no_access sign_up_requires_approval sign_up_block), desc: 'Severity of the block' option :severity, required: true, enum: IpBlock.severities.keys, desc: 'Severity of the block'
option :comment, aliases: [:c], desc: 'Optional comment' option :comment, aliases: [:c], desc: 'Optional comment'
option :duration, aliases: [:d], type: :numeric, desc: 'Duration of the block in seconds' option :duration, aliases: [:d], type: :numeric, desc: 'Duration of the block in seconds'
option :force, type: :boolean, aliases: [:f], desc: 'Overwrite existing blocks' option :force, type: :boolean, aliases: [:f], desc: 'Overwrite existing blocks'

View File

@ -7,8 +7,19 @@ namespace :db do
namespace :encryption do namespace :encryption do
desc 'Generate a set of keys for configuring Active Record encryption in a given environment' desc 'Generate a set of keys for configuring Active Record encryption in a given environment'
task :init do # rubocop:disable Rails/RakeEnvironment task :init do # rubocop:disable Rails/RakeEnvironment
if %w(
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
).any? { |key| ENV.key?(key) }
pastel = Pastel.new
puts pastel.red(<<~MSG)
WARNING: It looks like encryption secrets have already been set. Please ensure you are not changing secrets for a Mastodon installation that already uses them, as this will cause data loss and other issues that are difficult to recover from.
MSG
end
puts <<~MSG puts <<~MSG
Add these secret environment variables to your Mastodon environment (e.g. .env.production):#{' '} Add the following secret environment variables to your Mastodon environment (e.g. .env.production), ensure they are shared across all your nodes and do not change them after they are set:#{' '}
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=#{SecureRandom.alphanumeric(32)} ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=#{SecureRandom.alphanumeric(32)}
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=#{SecureRandom.alphanumeric(32)} ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=#{SecureRandom.alphanumeric(32)}

View File

@ -1,82 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::TagsController do
render_views
before do
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
end
describe 'GET #index' do
before do
Fabricate(:tag)
tag_filter = instance_double(Admin::TagFilter, results: Tag.all)
allow(Admin::TagFilter).to receive(:new).and_return(tag_filter)
end
let(:params) { { order: 'newest' } }
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
expect(response).to render_template(:index)
expect(Admin::TagFilter)
.to have_received(:new)
.with(hash_including(params))
end
describe 'with filters' do
let(:params) { { order: 'newest', name: 'test' } }
it 'returns http success' do
get :index, params: { name: 'test' }
expect(response).to have_http_status(200)
expect(response).to render_template(:index)
expect(Admin::TagFilter)
.to have_received(:new)
.with(hash_including(params))
end
end
end
describe 'GET #show' do
let!(:tag) { Fabricate(:tag) }
before do
get :show, params: { id: tag.id }
end
it 'returns status 200' do
expect(response).to have_http_status(200)
end
end
describe 'PUT #update' do
let!(:tag) { Fabricate(:tag, listable: false) }
context 'with valid params' do
it 'updates the tag' do
put :update, params: { id: tag.id, tag: { listable: '1' } }
expect(response).to redirect_to(admin_tag_path(tag.id))
expect(tag.reload).to be_listable
end
end
context 'with invalid params' do
it 'does not update the tag' do
put :update, params: { id: tag.id, tag: { name: 'cant-change-name' } }
expect(response).to have_http_status(200)
expect(response).to render_template(:show)
end
end
end
end

View File

@ -3,14 +3,13 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe DomainBlock do RSpec.describe DomainBlock do
describe 'validations' do describe 'Validations' do
it { is_expected.to validate_presence_of(:domain) } it { is_expected.to validate_presence_of(:domain) }
it 'is invalid if the same normalized domain already exists' do context 'when a normalized domain exists' do
_domain_block = Fabricate(:domain_block, domain: 'にゃん') before { Fabricate(:domain_block, domain: 'にゃん') }
domain_block_with_normalized_value = Fabricate.build(:domain_block, domain: 'xn--r9j5b5b')
domain_block_with_normalized_value.valid? it { is_expected.to_not allow_value('xn--r9j5b5b').for(:domain) }
expect(domain_block_with_normalized_value).to model_have_error_on_field(:domain)
end end
end end
@ -105,4 +104,26 @@ RSpec.describe DomainBlock do
end end
end end
end end
describe '#policies' do
subject { domain_block.policies }
context 'when severity is suspend' do
let(:domain_block) { Fabricate.build :domain_block, severity: :suspend }
it { is_expected.to eq(%i(suspend)) }
end
context 'when severity is noop' do
let(:domain_block) { Fabricate.build :domain_block, severity: :noop, reject_media: true }
it { is_expected.to eq(%i(reject_media)) }
end
context 'when severity is silence' do
let(:domain_block) { Fabricate.build :domain_block, severity: :silence, reject_reports: true }
it { is_expected.to eq(%i(silence reject_reports)) }
end
end
end end

View File

@ -7,19 +7,25 @@ RSpec.describe NotificationPolicy do
subject { Fabricate(:notification_policy) } subject { Fabricate(:notification_policy) }
let(:sender) { Fabricate(:account) } let(:sender) { Fabricate(:account) }
let(:suspended_sender) { Fabricate(:account) }
before do before do
Fabricate.times(2, :notification, account: subject.account, activity: Fabricate(:status, account: sender), filtered: true, type: :mention) Fabricate.times(2, :notification, account: subject.account, activity: Fabricate(:status, account: sender), filtered: true, type: :mention)
Fabricate(:notification_request, account: subject.account, from_account: sender) Fabricate(:notification_request, account: subject.account, from_account: sender)
Fabricate(:notification, account: subject.account, activity: Fabricate(:status, account: suspended_sender), filtered: true, type: :mention)
Fabricate(:notification_request, account: subject.account, from_account: suspended_sender)
suspended_sender.suspend!
subject.summarize! subject.summarize!
end end
it 'sets pending_requests_count' do it 'sets pending_requests_count and pending_notifications_count' do
expect(subject.pending_requests_count).to eq 1 expect(subject).to have_attributes(
end pending_requests_count: 1,
pending_notifications_count: 2
it 'sets pending_notifications_count' do )
expect(subject.pending_notifications_count).to eq 2
end end
end end
end end

View File

@ -559,11 +559,53 @@ RSpec.describe Status do
end end
end end
describe 'validation' do describe 'Validations' do
it 'disallow empty uri for remote status' do context 'with a remote account' do
alice.update(domain: 'example.com') subject { Fabricate.build :status, account: remote_account }
status = Fabricate.build(:status, uri: '', account: alice)
expect(status).to model_have_error_on_field(:uri) let(:remote_account) { Fabricate :account, domain: 'example.com' }
it { is_expected.to_not allow_value('').for(:uri) }
end
end
describe 'Callbacks' do
describe 'Stripping content when required' do
context 'with a remote account' do
subject { Fabricate.build :status, local: false, account:, text: ' text ', spoiler_text: ' spoiler ' }
let(:account) { Fabricate.build :account, domain: 'host.example' }
it 'preserves content' do
expect { subject.valid? }
.to not_change(subject, :text)
.and not_change(subject, :spoiler_text)
end
end
context 'with a local account' do
let(:account) { Fabricate.build :account, domain: nil }
context 'with populated fields' do
subject { Fabricate.build :status, local: true, account:, text: ' text ', spoiler_text: ' spoiler ' }
it 'strips content' do
expect { subject.valid? }
.to change(subject, :text).to('text')
.and change(subject, :spoiler_text).to('spoiler')
end
end
context 'with empty fields' do
subject { Fabricate.build :status, local: true, account:, text: nil, spoiler_text: nil }
it 'preserves content' do
expect { subject.valid? }
.to not_change(subject, :text)
.and not_change(subject, :spoiler_text)
end
end
end
end end
end end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Admin Tags' do
describe 'Tag interaction' do
let!(:tag) { Fabricate(:tag, name: 'test') }
before { sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
it 'allows tags listing and editing' do
visit admin_tags_path
expect(page)
.to have_title(I18n.t('admin.tags.title'))
click_on '#test'
fill_in display_name_field, with: 'NewTagName'
expect { click_on submit_button }
.to_not(change { tag.reload.display_name })
expect(page)
.to have_content(match_error_text)
fill_in display_name_field, with: 'TEST'
expect { click_on submit_button }
.to(change { tag.reload.display_name }.to('TEST'))
end
def display_name_field
I18n.t('simple_form.labels.defaults.display_name')
end
def match_error_text
I18n.t('tags.does_not_match_previous_name')
end
end
end

View File

@ -24,28 +24,28 @@ RSpec.describe 'Using OAuth from an external app' do
subject subject
# It presents the user with an authorization page # It presents the user with an authorization page
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(oauth_authorize_text)
# Upon authorizing, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be true expect { click_on oauth_authorize_text }
.to change { user_has_grant_with_client_app? }.to(true)
# Upon authorizing, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
it 'when rejecting the authorization request' do it 'when rejecting the authorization request' do
subject subject
# It presents the user with an authorization page # It presents the user with an authorization page
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.deny')) expect(page).to have_content(oauth_deny_text)
# Upon denying, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be false expect { click_on oauth_deny_text }
.to_not change { user_has_grant_with_client_app? }.from(false)
# Upon denying, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
# The tests in this context ensures that requests without PKCE parameters # The tests in this context ensures that requests without PKCE parameters
@ -133,7 +133,6 @@ RSpec.describe 'Using OAuth from an external app' do
end end
it 'when accepting the authorization request' do it 'when accepting the authorization request' do
params = { client_id: client_app.uid, response_type: 'code', redirect_uri: client_app.redirect_uri, scope: 'read' }
visit "/oauth/authorize?#{params.to_query}" visit "/oauth/authorize?#{params.to_query}"
# It presents the user with a log-in page # It presents the user with a log-in page
@ -145,18 +144,17 @@ RSpec.describe 'Using OAuth from an external app' do
# Logging in redirects to an authorization page # Logging in redirects to an authorization page
fill_in_auth_details(email, password) fill_in_auth_details(email, password)
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(oauth_authorize_text)
# Upon authorizing, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be true expect { click_on oauth_authorize_text }
.to change { user_has_grant_with_client_app? }.to(true)
# Upon authorizing, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
it 'when rejecting the authorization request' do it 'when rejecting the authorization request' do
params = { client_id: client_app.uid, response_type: 'code', redirect_uri: client_app.redirect_uri, scope: 'read' }
visit "/oauth/authorize?#{params.to_query}" visit "/oauth/authorize?#{params.to_query}"
# It presents the user with a log-in page # It presents the user with a log-in page
@ -168,21 +166,20 @@ RSpec.describe 'Using OAuth from an external app' do
# Logging in redirects to an authorization page # Logging in redirects to an authorization page
fill_in_auth_details(email, password) fill_in_auth_details(email, password)
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(oauth_authorize_text)
# Upon denying, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be false expect { click_on oauth_deny_text }
.to_not change { user_has_grant_with_client_app? }.from(false)
# Upon denying, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
context 'when the user has set up TOTP' do context 'when the user has set up TOTP' do
let(:user) { Fabricate(:user, email: email, password: password, otp_required_for_login: true, otp_secret: User.generate_otp_secret) } let(:user) { Fabricate(:user, email: email, password: password, otp_required_for_login: true, otp_secret: User.generate_otp_secret) }
it 'when accepting the authorization request' do it 'when accepting the authorization request' do
params = { client_id: client_app.uid, response_type: 'code', redirect_uri: client_app.redirect_uri, scope: 'read' }
visit "/oauth/authorize?#{params.to_query}" visit "/oauth/authorize?#{params.to_query}"
# It presents the user with a log-in page # It presents the user with a log-in page
@ -202,18 +199,17 @@ RSpec.describe 'Using OAuth from an external app' do
# Filling in the correct TOTP code redirects to an app authorization page # Filling in the correct TOTP code redirects to an app authorization page
fill_in_otp_details(user.current_otp) fill_in_otp_details(user.current_otp)
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(oauth_authorize_text)
# Upon authorizing, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be true expect { click_on oauth_authorize_text }
.to change { user_has_grant_with_client_app? }.to(true)
# Upon authorizing, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
it 'when rejecting the authorization request' do it 'when rejecting the authorization request' do
params = { client_id: client_app.uid, response_type: 'code', redirect_uri: client_app.redirect_uri, scope: 'read' }
visit "/oauth/authorize?#{params.to_query}" visit "/oauth/authorize?#{params.to_query}"
# It presents the user with a log-in page # It presents the user with a log-in page
@ -233,14 +229,14 @@ RSpec.describe 'Using OAuth from an external app' do
# Filling in the correct TOTP code redirects to an app authorization page # Filling in the correct TOTP code redirects to an app authorization page
fill_in_otp_details(user.current_otp) fill_in_otp_details(user.current_otp)
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(oauth_authorize_text)
# Upon denying, it redirects to the apps' callback URL
click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account
expect(Doorkeeper::AccessGrant.exists?(application: client_app, resource_owner_id: user.id)).to be false expect { click_on oauth_deny_text }
.to_not change { user_has_grant_with_client_app? }.from(false)
# Upon denying, it redirects to the apps' callback URL
expect(page).to redirect_to_callback_url
end end
end end
# TODO: external auth # TODO: external auth
@ -252,4 +248,24 @@ RSpec.describe 'Using OAuth from an external app' do
fill_in 'user_otp_attempt', with: value fill_in 'user_otp_attempt', with: value
click_on I18n.t('auth.login') click_on I18n.t('auth.login')
end end
def oauth_authorize_text
I18n.t('doorkeeper.authorizations.buttons.authorize')
end
def oauth_deny_text
I18n.t('doorkeeper.authorizations.buttons.deny')
end
def redirect_to_callback_url
have_current_path(/\A#{client_app.redirect_uri}/, url: true)
end
def user_has_grant_with_client_app?
Doorkeeper::AccessGrant
.exists?(
application: client_app,
resource_owner_id: user.id
)
end
end end

532
yarn.lock
View File

@ -1533,13 +1533,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/cascade-layer-name-parser@npm:^2.0.1": "@csstools/cascade-layer-name-parser@npm:^2.0.2":
version: 2.0.1 version: 2.0.2
resolution: "@csstools/cascade-layer-name-parser@npm:2.0.1" resolution: "@csstools/cascade-layer-name-parser@npm:2.0.2"
peerDependencies: peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.1 "@csstools/css-parser-algorithms": ^3.0.2
"@csstools/css-tokenizer": ^3.0.1 "@csstools/css-tokenizer": ^3.0.2
checksum: 10c0/e1f9030285d1a16ca0424018289e5288e58dee2d6f6cc392e27d9e8eca0deeea4ced9b749eef09a8322746cb15b6304bc7e2d04bb9dc7405c29b3717ec80e736 checksum: 10c0/2cc840445328400bb3e1e4186e6081e6519a23d9abde36a16c95892b6ad75155b3af3410d79fdda1c53a068384f970cabff4b5f5ba6867578168cbd3419016c8
languageName: node languageName: node
linkType: hard linkType: hard
@ -1550,42 +1550,42 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/css-calc@npm:^2.0.1": "@csstools/css-calc@npm:^2.0.2":
version: 2.0.1 version: 2.0.2
resolution: "@csstools/css-calc@npm:2.0.1" resolution: "@csstools/css-calc@npm:2.0.2"
peerDependencies: peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.1 "@csstools/css-parser-algorithms": ^3.0.2
"@csstools/css-tokenizer": ^3.0.1 "@csstools/css-tokenizer": ^3.0.2
checksum: 10c0/84c0ba3dac51466c9ac22c3540360f6058cf351a3676d71d4412584b165a91abc7c69a4ddf5a5dacc6e6082806d2317cf50ddbc0a0562275b2aedaee41cb87a9 checksum: 10c0/b36e655b4abc8ea39b300725e33cd43b1875d759dd60bee8155bf7841065615a7f24cf53199382e30aa10bb137f64021043e4af7e11b7199b674a6e6cf3ccd01
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/css-color-parser@npm:^3.0.2": "@csstools/css-color-parser@npm:^3.0.3":
version: 3.0.2 version: 3.0.3
resolution: "@csstools/css-color-parser@npm:3.0.2" resolution: "@csstools/css-color-parser@npm:3.0.3"
dependencies: dependencies:
"@csstools/color-helpers": "npm:^5.0.1" "@csstools/color-helpers": "npm:^5.0.1"
"@csstools/css-calc": "npm:^2.0.1" "@csstools/css-calc": "npm:^2.0.2"
peerDependencies: peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.1 "@csstools/css-parser-algorithms": ^3.0.2
"@csstools/css-tokenizer": ^3.0.1 "@csstools/css-tokenizer": ^3.0.2
checksum: 10c0/31f42cc22426c937f5c6999889f72b40aec8504189a6badf3319552f27a5af73dd5f46be9ebc54eb87e526468eb2546ffbd60a6879fe599efa6c98e51d6cfa3d checksum: 10c0/02367ffc222254132c47f9cbc856f65fe0b81ee4a5e7381251b95c4064138b5ed99a5e5a79c0c8689f9e75e3d900f94773258a161a97f467c3f0420838c10e04
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/css-parser-algorithms@npm:^3.0.1": "@csstools/css-parser-algorithms@npm:^3.0.1, @csstools/css-parser-algorithms@npm:^3.0.2":
version: 3.0.1 version: 3.0.2
resolution: "@csstools/css-parser-algorithms@npm:3.0.1" resolution: "@csstools/css-parser-algorithms@npm:3.0.2"
peerDependencies: peerDependencies:
"@csstools/css-tokenizer": ^3.0.1 "@csstools/css-tokenizer": ^3.0.2
checksum: 10c0/064c6d519197b5af43bbf5efe8f4cdbd361b006113aa82160d637e925b50c643a52d33d512ca01c63042d952d723a2a10798231a714668356b76668fb11294e3 checksum: 10c0/246afbf518ee9eaa24ed7f083360eb66884f1172fd4f8c663bff8c6099de2a8abd1e2a31d5b6fe42e010277d238469d780cff62bc7fdc6a52e7a90626b8924dc
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/css-tokenizer@npm:^3.0.1": "@csstools/css-tokenizer@npm:^3.0.1, @csstools/css-tokenizer@npm:^3.0.2":
version: 3.0.1 version: 3.0.2
resolution: "@csstools/css-tokenizer@npm:3.0.1" resolution: "@csstools/css-tokenizer@npm:3.0.2"
checksum: 10c0/c9ed4373e5731b5375ea9791590081019c04e95f08b46b272977e5e7b8c3d560affc62e82263cb8def1df1e57f0673140e7e16a14a5e7be04e6a234be088d1d3 checksum: 10c0/a74e5829420ed35982fd33be272c2a19cb2380179d357abe750aa848be6d6699d0437008f47a57eb7c6ff64a34b0c8f91a97dd63dbddd08249b7cf7983767e5e
languageName: node languageName: node
linkType: hard linkType: hard
@ -1599,6 +1599,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/media-query-list-parser@npm:^4.0.0":
version: 4.0.0
resolution: "@csstools/media-query-list-parser@npm:4.0.0"
peerDependencies:
"@csstools/css-parser-algorithms": ^3.0.2
"@csstools/css-tokenizer": ^3.0.2
checksum: 10c0/416417bcfd84c18a2df8dc77f31c87830e151dc20530fe7f0d8f13a0848b1a9090858abdf7792d82bf2edb41ddedb7b57b34eb78b68b5c10755ae02c019e496a
languageName: node
linkType: hard
"@csstools/postcss-cascade-layers@npm:^5.0.0": "@csstools/postcss-cascade-layers@npm:^5.0.0":
version: 5.0.0 version: 5.0.0
resolution: "@csstools/postcss-cascade-layers@npm:5.0.0" resolution: "@csstools/postcss-cascade-layers@npm:5.0.0"
@ -1611,60 +1621,60 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-color-function@npm:^4.0.2": "@csstools/postcss-color-function@npm:^4.0.3":
version: 4.0.2 version: 4.0.3
resolution: "@csstools/postcss-color-function@npm:4.0.2" resolution: "@csstools/postcss-color-function@npm:4.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/c987ccc7ab326668895396d3fe69c05087cf6e245be6f70e8c33da0cdeb56f8ac3d117cfad984191ec57be9691ab56d427aaa28c61c4a7446521972993dd5039 checksum: 10c0/c994660ca0e2652755d9ad181c8cb46a07220c972086c97c843fa9bacf690be10c642770f898aeec4acc47c2b718dfc7372107285a678361f34d84d9e9c11e0c
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-color-mix-function@npm:^3.0.2": "@csstools/postcss-color-mix-function@npm:^3.0.3":
version: 3.0.2 version: 3.0.3
resolution: "@csstools/postcss-color-mix-function@npm:3.0.2" resolution: "@csstools/postcss-color-mix-function@npm:3.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/cfdc641f504f9d02b9a7b53d6bd933d4e767ecaee5f3d2df45d897f69b8a38b5b79b538d307b16fb56dcb7c19dc7e107518c356772e89771e28e04fd846d9035 checksum: 10c0/4ba358eb9030fc485bfe2922d897eeb712725762cc399eaba60ba665c84dc3e56a4d5a52dfb320093c0b217d32fedb9b5197fa45738cade53d9afcbefdadf04f
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-content-alt-text@npm:^2.0.1": "@csstools/postcss-content-alt-text@npm:^2.0.2":
version: 2.0.1 version: 2.0.2
resolution: "@csstools/postcss-content-alt-text@npm:2.0.1" resolution: "@csstools/postcss-content-alt-text@npm:2.0.2"
dependencies: dependencies:
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/693e4cfa9a30a9c384120bd24846a7cd5ba1e1ebf975eb81b0e0131a21ac28a0301c7dcfa13706bc7d8b5343536fb43a46de636c3257d0fd05041c7255366e87 checksum: 10c0/e52d40f6567b9b23b32a6c40f9b2a74d57f99a9921b4cae015f51f72453474236c760bb13120682f8815698a615e0ad7bed22314c29dca89c34b5480d83a7a6d
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-exponential-functions@npm:^2.0.1": "@csstools/postcss-exponential-functions@npm:^2.0.2":
version: 2.0.1 version: 2.0.2
resolution: "@csstools/postcss-exponential-functions@npm:2.0.1" resolution: "@csstools/postcss-exponential-functions@npm:2.0.2"
dependencies: dependencies:
"@csstools/css-calc": "npm:^2.0.1" "@csstools/css-calc": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/ddcaedfa48cc0cf93611c8d2ed5a75d56c1d196a97015db644b45881adabb47f3255242acaef6ea869a1e5ba66328725d254bf6d29eb5e988cde8b040bc5f55d checksum: 10c0/034ff89089872f63a6b00bda670c5ff11361babd221ed3e441dde969a718059e5d44ab0ed331868f137bb205331b808ecbcc4cb641d5c945238ebca28aa3ed59
languageName: node languageName: node
linkType: hard linkType: hard
@ -1680,46 +1690,46 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-gamut-mapping@npm:^2.0.2": "@csstools/postcss-gamut-mapping@npm:^2.0.3":
version: 2.0.2 version: 2.0.3
resolution: "@csstools/postcss-gamut-mapping@npm:2.0.2" resolution: "@csstools/postcss-gamut-mapping@npm:2.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/8b6504f81c5036e3c2a9f9516c371f48a283112469b746546c8c7f6f0da2467c915d4dac6dfe8bb05d7dab3a7503911391eb9e666cb7632e09a032e801c029f5 checksum: 10c0/21f5708f63e9c3b7603f8b72b5f288e0a021e9710a6abf4aaa713ff4d04bae057d1861e1f28d7670ea39ba463ac04f1507876d4a11178934e7cc7a1c6a780084
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-gradients-interpolation-method@npm:^5.0.2": "@csstools/postcss-gradients-interpolation-method@npm:^5.0.3":
version: 5.0.2 version: 5.0.3
resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.2" resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/4fa27437ad9861b1457c28228f2503c17bcc2f77dcb38da1314a4a5d38fd010e7e5d11b5f9d69e0a2cb2999bbfeca1e99ce2f59422bda5b382658dcb03f7326e checksum: 10c0/062d27148438309c940a1973bfc7d42a06caa9397bf2382c7a61979f5be3d6f3fae1bc8ddf94d2dd8e6c807e0530a9e76179510266aaddc439677bf79447a765
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-hwb-function@npm:^4.0.2": "@csstools/postcss-hwb-function@npm:^4.0.3":
version: 4.0.2 version: 4.0.3
resolution: "@csstools/postcss-hwb-function@npm:4.0.2" resolution: "@csstools/postcss-hwb-function@npm:4.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/46dc9596e37830de4c38f70764d6da9f2fc7bc339217b4291eced75daa8998c4e05fb743c271701e44818df4ac111c285019b7bb3a728e8b61d86899bbeb74eb checksum: 10c0/faf2bfbafeec765391e37c7a5cbc7b4647d9ab1ffa51e922c7dfffa545c3d436d15604dfdfb9d7684e760042e62bb42e0243dd4ebd8c3c14694a9f7be4e57b30
languageName: node languageName: node
linkType: hard linkType: hard
@ -1757,17 +1767,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-light-dark-function@npm:^2.0.4": "@csstools/postcss-light-dark-function@npm:^2.0.5":
version: 2.0.4 version: 2.0.5
resolution: "@csstools/postcss-light-dark-function@npm:2.0.4" resolution: "@csstools/postcss-light-dark-function@npm:2.0.5"
dependencies: dependencies:
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/0176422ad9747953964b1ceff002df1ecb1952ebc481db6192070d68777135b582ea6fd32ae819b9c64c96cb9170bd6907c647c85b48daa4984b7ed3d7f9bccb checksum: 10c0/80635ee312d2a8f42aa5ce6792f1dc4a71199c384c66a3270d37e998d96db55beaa6836d689cda3b7e4828227960582fae04659ba5e4e0f64fd4543cbf15c6ab
languageName: node languageName: node
linkType: hard linkType: hard
@ -1809,42 +1819,42 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-logical-viewport-units@npm:^3.0.1": "@csstools/postcss-logical-viewport-units@npm:^3.0.2":
version: 3.0.1 version: 3.0.2
resolution: "@csstools/postcss-logical-viewport-units@npm:3.0.1" resolution: "@csstools/postcss-logical-viewport-units@npm:3.0.2"
dependencies: dependencies:
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/f54f91ec4d308562371576e82131c3cc1ff461a951c9a38f0b42b783c26f37a93cc846fcd025d3c4a8437b55a4fff1192ebfac8ccf84abb6478b2c515d232552 checksum: 10c0/31f525e774bc053f545a159eb53bb21465ea2930118e87c40207ad90fa97d3151e6de83efd02f84803fb0e93ed4a4b42a3904b734423410e73ac4c6ce9666ab4
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-media-minmax@npm:^2.0.1": "@csstools/postcss-media-minmax@npm:^2.0.2":
version: 2.0.1 version: 2.0.2
resolution: "@csstools/postcss-media-minmax@npm:2.0.1" resolution: "@csstools/postcss-media-minmax@npm:2.0.2"
dependencies: dependencies:
"@csstools/css-calc": "npm:^2.0.1" "@csstools/css-calc": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/media-query-list-parser": "npm:^3.0.1" "@csstools/media-query-list-parser": "npm:^4.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/23c1fb0c3ed8bf82f3223f161d0d65ba62045b917bc19624581f64aaa5d678485d22c23af2591a3f634ba02030ccb419c2b30209aa22f9fd2baa1a6474af810a checksum: 10c0/83cf10742884fca3baa7ae26e2cb34123ce5a022622390566c139b4587ea8583fab00acbb85545786b36398e2201d2a94301e0fae805e55f375f1b5c38f67ce8
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.1": "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.2":
version: 3.0.1 version: 3.0.2
resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.1" resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.2"
dependencies: dependencies:
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/media-query-list-parser": "npm:^3.0.1" "@csstools/media-query-list-parser": "npm:^4.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/e491cb149fb4fff85b2f03191511e43654ae00716e3c1ea9f1dc22ec4e7042c35f034d372082a69d3621c86cafbe46e8f419872fa36f4a534f145f584d655768 checksum: 10c0/3ac4073d2e958bfb24ae45f673070dd805f0fcf07bc8d00a9a98f596d1096e7be282c8d8e87df3abde90f33fcbe2c7705e972b8c1a58e43ec44729f470b76096
languageName: node languageName: node
linkType: hard linkType: hard
@ -1871,18 +1881,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-oklab-function@npm:^4.0.2": "@csstools/postcss-oklab-function@npm:^4.0.3":
version: 4.0.2 version: 4.0.3
resolution: "@csstools/postcss-oklab-function@npm:4.0.2" resolution: "@csstools/postcss-oklab-function@npm:4.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/3209a7cec6d3577544a7ef41f2d5cca25f77891d4ec0d7f39d32f9a79a6c9a9b0ee6b54b2937a2d995548ad11c39966b07d4b9f58e907ffbe1a4b454f2d277f3 checksum: 10c0/650bcb4f664308972588a8f789f806d63c4069e2e008cfc3b5c80bf9df992c62972dce279b8f434c7f78823e97095942ee4f0e37bc549258887213e72acb7ef8
languageName: node languageName: node
linkType: hard linkType: hard
@ -1897,18 +1907,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-relative-color-syntax@npm:^3.0.2": "@csstools/postcss-relative-color-syntax@npm:^3.0.3":
version: 3.0.2 version: 3.0.3
resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.2" resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/34a8c999e08c6e7833484ee2fb91e7fcc25235d6c361712fed581e44a5a29f1ceb95415b6f4260de53809ac13f5da5415d1905c2971477cf5d45e5196081c663 checksum: 10c0/c241fe6b725d775f6d2085be1dff3868d189b176fa91ab1eb1133e30e30c8151bded4e50d17a845edd0bdd0a7adf9e8883cb2634fea3394872591fe9ad2a7e86
languageName: node languageName: node
linkType: hard linkType: hard
@ -1923,16 +1933,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-stepped-value-functions@npm:^4.0.1": "@csstools/postcss-stepped-value-functions@npm:^4.0.2":
version: 4.0.1 version: 4.0.2
resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.1" resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.2"
dependencies: dependencies:
"@csstools/css-calc": "npm:^2.0.1" "@csstools/css-calc": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/7e65969b124fce603675ca17c2ffa2bb456677866e54bc9fbdc4da0945be1593fde2abb0730d3d03190776ad2022b394a1f9d4834c5b1f4c7ec497929fd35f8f checksum: 10c0/444a27d725bc7a8e1554469e8ac69e248ff525b728fbe058523b0f1aefcff80ca707f543d21fead0a22d51603b1669190fb01f0f2dcd599a01768a37e0d62bc3
languageName: node languageName: node
linkType: hard linkType: hard
@ -1948,16 +1958,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@csstools/postcss-trigonometric-functions@npm:^4.0.1": "@csstools/postcss-trigonometric-functions@npm:^4.0.2":
version: 4.0.1 version: 4.0.2
resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.1" resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.2"
dependencies: dependencies:
"@csstools/css-calc": "npm:^2.0.1" "@csstools/css-calc": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/59e017ebb9f4f8f027e134024e3322b5e202cc96073e0bb0d45733a829c8eadc7f4f7ce57ce8360a748a677595af9ea95da1779684699b48b911b73b4017ac8b checksum: 10c0/eaecb2ea891162e4fcbbccf4f660c99e9e59f21937b70fe6aec3b51441eff2a12c1a2dc13fff426722629a7929919fd866311eaa68d74ee9d1f5387a23502fe2
languageName: node languageName: node
linkType: hard linkType: hard
@ -2257,8 +2267,8 @@ __metadata:
linkType: hard linkType: hard
"@formatjs/cli@npm:^6.1.1": "@formatjs/cli@npm:^6.1.1":
version: 6.2.12 version: 6.2.15
resolution: "@formatjs/cli@npm:6.2.12" resolution: "@formatjs/cli@npm:6.2.15"
peerDependencies: peerDependencies:
"@glimmer/env": ^0.1.7 "@glimmer/env": ^0.1.7
"@glimmer/reference": ^0.91.1 || ^0.92.0 "@glimmer/reference": ^0.91.1 || ^0.92.0
@ -2287,7 +2297,7 @@ __metadata:
optional: true optional: true
bin: bin:
formatjs: bin/formatjs formatjs: bin/formatjs
checksum: 10c0/3bd05a9fad6c837e22988e6638f426c128efa46ab80ff88cf2ad81fb3bc10cf4f228907577fc01e24c2d7d505cfabfaa69f0496d2ec8f0ab2d6b5eaccb5e475c checksum: 10c0/e947aa7f3994251392fe15673752a8d8e3c8a30733bb49de5e617d45a327a3e1d16419e2d6b01f7ef2cbe86e2946024342d5b3301e6a8f17de3de9e2e7aedb29
languageName: node languageName: node
linkType: hard linkType: hard
@ -3113,8 +3123,8 @@ __metadata:
linkType: hard linkType: hard
"@reduxjs/toolkit@npm:^2.0.1": "@reduxjs/toolkit@npm:^2.0.1":
version: 2.2.7 version: 2.2.8
resolution: "@reduxjs/toolkit@npm:2.2.7" resolution: "@reduxjs/toolkit@npm:2.2.8"
dependencies: dependencies:
immer: "npm:^10.0.3" immer: "npm:^10.0.3"
redux: "npm:^5.0.1" redux: "npm:^5.0.1"
@ -3128,7 +3138,7 @@ __metadata:
optional: true optional: true
react-redux: react-redux:
optional: true optional: true
checksum: 10c0/7761a91adac2b5e1d50a8163ba5441480bb86a3a80b7583037c27a88463394b132dd7592862fc2be03aa7ab98a6e1710549889986dc0d3f033c169a3ba2cb02e checksum: 10c0/bf1356d71bfb82e5a181692c79c19b7bc19355260a9966f6562604c995f0cd0ce1154177ccd14095e8b319e73f64cfe86a4e46a83d24edba7876d4ae71fd5ae0
languageName: node languageName: node
linkType: hard linkType: hard
@ -6402,10 +6412,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cookie@npm:0.6.0": "cookie@npm:0.7.1":
version: 0.6.0 version: 0.7.1
resolution: "cookie@npm:0.6.0" resolution: "cookie@npm:0.7.1"
checksum: 10c0/f2318b31af7a31b4ddb4a678d024514df5e705f9be5909a192d7f116cfb6d45cbacf96a473fa733faa95050e7cff26e7832bb3ef94751592f1387b71c8956686 checksum: 10c0/5de60c67a410e7c8dc8a46a4b72eb0fe925871d057c9a5d2c0e8145c4270a4f81076de83410c4d397179744b478e33cd80ccbcc457abf40a9409ad27dcd21dde
languageName: node languageName: node
linkType: hard linkType: hard
@ -6622,10 +6632,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"css-functions-list@npm:^3.2.2": "css-functions-list@npm:^3.2.3":
version: 3.2.2 version: 3.2.3
resolution: "css-functions-list@npm:3.2.2" resolution: "css-functions-list@npm:3.2.3"
checksum: 10c0/8638a63d0cf1bdc50d4a752ec1c94a57e9953c3b03eace4f5526db20bec3c061e95089f905dbb4999c44b9780ce777ba856967560f6d15119a303f6030901c10 checksum: 10c0/03f9ed34eeed310d2b1cf0e524eea02bc5f87854a4de85f8957ea432ab1036841a3fb00879590519f7bb8fda40d992ce7a72fa9b61696ca1dc53b90064858f96
languageName: node languageName: node
linkType: hard linkType: hard
@ -6733,6 +6743,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"css-tree@npm:^3.0.0":
version: 3.0.0
resolution: "css-tree@npm:3.0.0"
dependencies:
mdn-data: "npm:2.10.0"
source-map-js: "npm:^1.0.1"
checksum: 10c0/43d44fdf7004ae91d73d486f17894fef77efa33747a6752b9241cf0f5fb47fabc16ec34a96a993651d9014dfdeee803d7c5fcd3548214252ee19f4e5c98999b2
languageName: node
linkType: hard
"css-tree@npm:~2.2.0": "css-tree@npm:~2.2.0":
version: 2.2.1 version: 2.2.1
resolution: "css-tree@npm:2.2.1" resolution: "css-tree@npm:2.2.1"
@ -6982,15 +7002,15 @@ __metadata:
languageName: node languageName: node
linkType: hard 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.5, debug@npm:^4.3.6, debug@npm:~4.3.6": "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.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:~4.3.6":
version: 4.3.6 version: 4.3.7
resolution: "debug@npm:4.3.6" resolution: "debug@npm:4.3.7"
dependencies: dependencies:
ms: "npm:2.1.2" ms: "npm:^2.1.3"
peerDependenciesMeta: peerDependenciesMeta:
supports-color: supports-color:
optional: true optional: true
checksum: 10c0/3293416bff072389c101697d4611c402a6bacd1900ac20c0492f61a9cdd6b3b29750fc7f5e299f8058469ef60ff8fb79b86395a30374fbd2490113c1c7112285 checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b
languageName: node languageName: node
linkType: hard linkType: hard
@ -8413,15 +8433,15 @@ __metadata:
linkType: hard linkType: hard
"express@npm:^4.17.1, express@npm:^4.18.2": "express@npm:^4.17.1, express@npm:^4.18.2":
version: 4.21.0 version: 4.21.1
resolution: "express@npm:4.21.0" resolution: "express@npm:4.21.1"
dependencies: dependencies:
accepts: "npm:~1.3.8" accepts: "npm:~1.3.8"
array-flatten: "npm:1.1.1" array-flatten: "npm:1.1.1"
body-parser: "npm:1.20.3" body-parser: "npm:1.20.3"
content-disposition: "npm:0.5.4" content-disposition: "npm:0.5.4"
content-type: "npm:~1.0.4" content-type: "npm:~1.0.4"
cookie: "npm:0.6.0" cookie: "npm:0.7.1"
cookie-signature: "npm:1.0.6" cookie-signature: "npm:1.0.6"
debug: "npm:2.6.9" debug: "npm:2.6.9"
depd: "npm:2.0.0" depd: "npm:2.0.0"
@ -8447,7 +8467,7 @@ __metadata:
type-is: "npm:~1.6.18" type-is: "npm:~1.6.18"
utils-merge: "npm:1.0.1" utils-merge: "npm:1.0.1"
vary: "npm:~1.1.2" vary: "npm:~1.1.2"
checksum: 10c0/4cf7ca328f3fdeb720f30ccb2ea7708bfa7d345f9cc460b64a82bf1b2c91e5b5852ba15a9a11b2a165d6089acf83457fc477dc904d59cd71ed34c7a91762c6cc checksum: 10c0/0c287867e5f6129d3def1edd9b63103a53c40d4dc8628839d4b6827e35eb8f0de5a4656f9d85f4457eba584f9871ebb2ad26c750b36bd75d9bbb8bcebdc4892c
languageName: node languageName: node
linkType: hard linkType: hard
@ -8591,12 +8611,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"file-entry-cache@npm:^9.0.0": "file-entry-cache@npm:^9.1.0":
version: 9.0.0 version: 9.1.0
resolution: "file-entry-cache@npm:9.0.0" resolution: "file-entry-cache@npm:9.1.0"
dependencies: dependencies:
flat-cache: "npm:^5.0.0" flat-cache: "npm:^5.0.0"
checksum: 10c0/07b0a4f062dc0aa258f3e1b06ac083ea25313f5e289943e146fafdaf3315dcc031635545eea7fe98fe5598b91d6c7f48dba7a251dd7ac20108a6ebf7d00b0b1c checksum: 10c0/4b4dbc1e972f50202b1a4430d30fd99378ef6e2a64857176abdc65c5e4730a948fb37e274478520a7bacbc70f3abba455a4b9d2c1915c53f30d11dc85d3fef5e
languageName: node languageName: node
linkType: hard linkType: hard
@ -8948,9 +8968,9 @@ __metadata:
linkType: hard linkType: hard
"fuzzysort@npm:^3.0.0": "fuzzysort@npm:^3.0.0":
version: 3.0.2 version: 3.1.0
resolution: "fuzzysort@npm:3.0.2" resolution: "fuzzysort@npm:3.1.0"
checksum: 10c0/c6cdbd092a8e91ed822aeac6d4fb95559759c10602cb29f27307c1cabd01fdd384fa399f7757722435b595244efb000cd63f144104c41b8551b2faff123279cb checksum: 10c0/da9bb32de16f2a5c2c000b99031d9f4f8a01380c12d5d3b67296443a1152c55987ce3c4ddbfe97481b0e9b6f2fb77d61dceba29a93ad36ee23ef5bab6a31afb8
languageName: node languageName: node
linkType: hard linkType: hard
@ -9670,13 +9690,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ignore@npm:^5.2.0, ignore@npm:^5.3.1, ignore@npm:^5.3.2": "ignore@npm:^5.2.0, ignore@npm:^5.3.1":
version: 5.3.2 version: 5.3.2
resolution: "ignore@npm:5.3.2" resolution: "ignore@npm:5.3.2"
checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
languageName: node languageName: node
linkType: hard linkType: hard
"ignore@npm:^6.0.2":
version: 6.0.2
resolution: "ignore@npm:6.0.2"
checksum: 10c0/9a38feac1861906a78ba0f03e8ef3cd6b0526dce2a1a84e1009324b557763afeb9c3ebcc04666b21f7bbf71adda45e76781bb9e2eaa0903d45dcaded634454f5
languageName: node
linkType: hard
"immer@npm:^10.0.3": "immer@npm:^10.0.3":
version: 10.0.3 version: 10.0.3
resolution: "immer@npm:10.0.3" resolution: "immer@npm:10.0.3"
@ -11842,6 +11869,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mdn-data@npm:2.10.0":
version: 2.10.0
resolution: "mdn-data@npm:2.10.0"
checksum: 10c0/f6f1a6a6eb092bab250d06f6f6c7cb1733a77a17e7119aac829ad67d4322bbf6a30df3c6d88686e71942e66bd49274b2ddfede22a1d3df0d6c49a56fbd09eb7c
languageName: node
linkType: hard
"media-typer@npm:0.3.0": "media-typer@npm:0.3.0":
version: 0.3.0 version: 0.3.0
resolution: "media-typer@npm:0.3.0" resolution: "media-typer@npm:0.3.0"
@ -12221,14 +12255,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ms@npm:2.1.2": "ms@npm:2.1.3, ms@npm:^2.1.1, ms@npm:^2.1.3":
version: 2.1.2
resolution: "ms@npm:2.1.2"
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
languageName: node
linkType: hard
"ms@npm:2.1.3, ms@npm:^2.1.1":
version: 2.1.3 version: 2.1.3
resolution: "ms@npm:2.1.3" resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
@ -13376,18 +13403,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-color-functional-notation@npm:^7.0.2": "postcss-color-functional-notation@npm:^7.0.3":
version: 7.0.2 version: 7.0.3
resolution: "postcss-color-functional-notation@npm:7.0.2" resolution: "postcss-color-functional-notation@npm:7.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/e89c0bff94558b0c978ac36f7e02f7f516291f90fd43169d39c63ad2bb0415e3b1c4b3c2469280d578727e850fdf15a557230cb28275f3f0676f0f73187f2867 checksum: 10c0/5e04c81002512c960784043c096bc91ebc76b8fddb9259a2418b0e121eb65042944cc0f78946f6b7e5774ff1fee087849019655e4848af1f88879e3ab9ff7c17
languageName: node languageName: node
linkType: hard linkType: hard
@ -13441,46 +13468,46 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-custom-media@npm:^11.0.2": "postcss-custom-media@npm:^11.0.3":
version: 11.0.2 version: 11.0.3
resolution: "postcss-custom-media@npm:11.0.2" resolution: "postcss-custom-media@npm:11.0.3"
dependencies: dependencies:
"@csstools/cascade-layer-name-parser": "npm:^2.0.1" "@csstools/cascade-layer-name-parser": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/media-query-list-parser": "npm:^3.0.1" "@csstools/media-query-list-parser": "npm:^4.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/7bec2b1e0b5d786c33c5715b611ffc8b9737252ee6bf77ca59255ac16f91ce614406923f43250e5c88b04f1bb050f155dc5ed4d9350dbd704c45fbd72e5a9a04 checksum: 10c0/bd3f0cf17d7422385d26afed510dc2acebb1d8c25fce13e2bbee1c49cdc7fe95ebe7f50b89ef0a88ebdd5f6826e89d99e26b905881ceff788df655670dba93d8
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-custom-properties@npm:^14.0.1": "postcss-custom-properties@npm:^14.0.2":
version: 14.0.1 version: 14.0.2
resolution: "postcss-custom-properties@npm:14.0.1" resolution: "postcss-custom-properties@npm:14.0.2"
dependencies: dependencies:
"@csstools/cascade-layer-name-parser": "npm:^2.0.1" "@csstools/cascade-layer-name-parser": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
postcss-value-parser: "npm:^4.2.0" postcss-value-parser: "npm:^4.2.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/12180a7f4a4fe2d528387346a1810b82ed870081756dcf6be226c839716ab3f6f4d6ca4c7208a07d7d84bf2c986beef6473e29964e7c2572066fca5d3b000ed5 checksum: 10c0/ea2e0cb60c558bb1afb4e601dcc64a38e1b28e5df3e47b83b858fc12d909d0e3453013e6b368fc05a7db7098ffcdc702a30a92f1a3c0ef67dfb97bf089021f1a
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-custom-selectors@npm:^8.0.1": "postcss-custom-selectors@npm:^8.0.2":
version: 8.0.1 version: 8.0.2
resolution: "postcss-custom-selectors@npm:8.0.1" resolution: "postcss-custom-selectors@npm:8.0.2"
dependencies: dependencies:
"@csstools/cascade-layer-name-parser": "npm:^2.0.1" "@csstools/cascade-layer-name-parser": "npm:^2.0.2"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
postcss-selector-parser: "npm:^6.1.0" postcss-selector-parser: "npm:^6.1.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/b867233b3d68fbab90afca8a776eb74196ebc3fac8988175d95118a47993c793138fec6cc580272bb35d9bd31086acbdd33ff86da0cab83ef2f08bfc1c23ecd6 checksum: 10c0/81673ffb0874f63c0f5e14315a5808259ec80ae8452aaf10d28112d30a9aaabbf61d13edb02f8be2965f44b943968c7eda051a1693da436ef157e77fcff0d752
languageName: node languageName: node
linkType: hard linkType: hard
@ -13598,18 +13625,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-lab-function@npm:^7.0.2": "postcss-lab-function@npm:^7.0.3":
version: 7.0.2 version: 7.0.3
resolution: "postcss-lab-function@npm:7.0.2" resolution: "postcss-lab-function@npm:7.0.3"
dependencies: dependencies:
"@csstools/css-color-parser": "npm:^3.0.2" "@csstools/css-color-parser": "npm:^3.0.3"
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.2"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.2"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/utilities": "npm:^2.0.0" "@csstools/utilities": "npm:^2.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/6b2be7e762b4ccb58ea9051723d390f6732ad78bb30bfef9499139cf5e2ac160c3de31b2b005fcc30e9fced4abe1685df6cb76c99d548896bae6746105ac8520 checksum: 10c0/c50a73a9ed54b4194998c4627599d1f42074235f572edbbcdb0e00717f3ae2121dc8378d917792b281860c5650a617d923823da6f395515f610b5760d115354d
languageName: node languageName: node
linkType: hard linkType: hard
@ -13930,38 +13957,38 @@ __metadata:
linkType: hard linkType: hard
"postcss-preset-env@npm:^10.0.0": "postcss-preset-env@npm:^10.0.0":
version: 10.0.6 version: 10.0.7
resolution: "postcss-preset-env@npm:10.0.6" resolution: "postcss-preset-env@npm:10.0.7"
dependencies: dependencies:
"@csstools/postcss-cascade-layers": "npm:^5.0.0" "@csstools/postcss-cascade-layers": "npm:^5.0.0"
"@csstools/postcss-color-function": "npm:^4.0.2" "@csstools/postcss-color-function": "npm:^4.0.3"
"@csstools/postcss-color-mix-function": "npm:^3.0.2" "@csstools/postcss-color-mix-function": "npm:^3.0.3"
"@csstools/postcss-content-alt-text": "npm:^2.0.1" "@csstools/postcss-content-alt-text": "npm:^2.0.2"
"@csstools/postcss-exponential-functions": "npm:^2.0.1" "@csstools/postcss-exponential-functions": "npm:^2.0.2"
"@csstools/postcss-font-format-keywords": "npm:^4.0.0" "@csstools/postcss-font-format-keywords": "npm:^4.0.0"
"@csstools/postcss-gamut-mapping": "npm:^2.0.2" "@csstools/postcss-gamut-mapping": "npm:^2.0.3"
"@csstools/postcss-gradients-interpolation-method": "npm:^5.0.2" "@csstools/postcss-gradients-interpolation-method": "npm:^5.0.3"
"@csstools/postcss-hwb-function": "npm:^4.0.2" "@csstools/postcss-hwb-function": "npm:^4.0.3"
"@csstools/postcss-ic-unit": "npm:^4.0.0" "@csstools/postcss-ic-unit": "npm:^4.0.0"
"@csstools/postcss-initial": "npm:^2.0.0" "@csstools/postcss-initial": "npm:^2.0.0"
"@csstools/postcss-is-pseudo-class": "npm:^5.0.0" "@csstools/postcss-is-pseudo-class": "npm:^5.0.0"
"@csstools/postcss-light-dark-function": "npm:^2.0.4" "@csstools/postcss-light-dark-function": "npm:^2.0.5"
"@csstools/postcss-logical-float-and-clear": "npm:^3.0.0" "@csstools/postcss-logical-float-and-clear": "npm:^3.0.0"
"@csstools/postcss-logical-overflow": "npm:^2.0.0" "@csstools/postcss-logical-overflow": "npm:^2.0.0"
"@csstools/postcss-logical-overscroll-behavior": "npm:^2.0.0" "@csstools/postcss-logical-overscroll-behavior": "npm:^2.0.0"
"@csstools/postcss-logical-resize": "npm:^3.0.0" "@csstools/postcss-logical-resize": "npm:^3.0.0"
"@csstools/postcss-logical-viewport-units": "npm:^3.0.1" "@csstools/postcss-logical-viewport-units": "npm:^3.0.2"
"@csstools/postcss-media-minmax": "npm:^2.0.1" "@csstools/postcss-media-minmax": "npm:^2.0.2"
"@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^3.0.1" "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^3.0.2"
"@csstools/postcss-nested-calc": "npm:^4.0.0" "@csstools/postcss-nested-calc": "npm:^4.0.0"
"@csstools/postcss-normalize-display-values": "npm:^4.0.0" "@csstools/postcss-normalize-display-values": "npm:^4.0.0"
"@csstools/postcss-oklab-function": "npm:^4.0.2" "@csstools/postcss-oklab-function": "npm:^4.0.3"
"@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0"
"@csstools/postcss-relative-color-syntax": "npm:^3.0.2" "@csstools/postcss-relative-color-syntax": "npm:^3.0.3"
"@csstools/postcss-scope-pseudo-class": "npm:^4.0.0" "@csstools/postcss-scope-pseudo-class": "npm:^4.0.0"
"@csstools/postcss-stepped-value-functions": "npm:^4.0.1" "@csstools/postcss-stepped-value-functions": "npm:^4.0.2"
"@csstools/postcss-text-decoration-shorthand": "npm:^4.0.1" "@csstools/postcss-text-decoration-shorthand": "npm:^4.0.1"
"@csstools/postcss-trigonometric-functions": "npm:^4.0.1" "@csstools/postcss-trigonometric-functions": "npm:^4.0.2"
"@csstools/postcss-unset-value": "npm:^4.0.0" "@csstools/postcss-unset-value": "npm:^4.0.0"
autoprefixer: "npm:^10.4.19" autoprefixer: "npm:^10.4.19"
browserslist: "npm:^4.23.1" browserslist: "npm:^4.23.1"
@ -13971,12 +13998,12 @@ __metadata:
cssdb: "npm:^8.1.1" cssdb: "npm:^8.1.1"
postcss-attribute-case-insensitive: "npm:^7.0.0" postcss-attribute-case-insensitive: "npm:^7.0.0"
postcss-clamp: "npm:^4.1.0" postcss-clamp: "npm:^4.1.0"
postcss-color-functional-notation: "npm:^7.0.2" postcss-color-functional-notation: "npm:^7.0.3"
postcss-color-hex-alpha: "npm:^10.0.0" postcss-color-hex-alpha: "npm:^10.0.0"
postcss-color-rebeccapurple: "npm:^10.0.0" postcss-color-rebeccapurple: "npm:^10.0.0"
postcss-custom-media: "npm:^11.0.2" postcss-custom-media: "npm:^11.0.3"
postcss-custom-properties: "npm:^14.0.1" postcss-custom-properties: "npm:^14.0.2"
postcss-custom-selectors: "npm:^8.0.1" postcss-custom-selectors: "npm:^8.0.2"
postcss-dir-pseudo-class: "npm:^9.0.0" postcss-dir-pseudo-class: "npm:^9.0.0"
postcss-double-position-gradients: "npm:^6.0.0" postcss-double-position-gradients: "npm:^6.0.0"
postcss-focus-visible: "npm:^10.0.0" postcss-focus-visible: "npm:^10.0.0"
@ -13984,7 +14011,7 @@ __metadata:
postcss-font-variant: "npm:^5.0.0" postcss-font-variant: "npm:^5.0.0"
postcss-gap-properties: "npm:^6.0.0" postcss-gap-properties: "npm:^6.0.0"
postcss-image-set-function: "npm:^7.0.0" postcss-image-set-function: "npm:^7.0.0"
postcss-lab-function: "npm:^7.0.2" postcss-lab-function: "npm:^7.0.3"
postcss-logical: "npm:^8.0.0" postcss-logical: "npm:^8.0.0"
postcss-nesting: "npm:^13.0.0" postcss-nesting: "npm:^13.0.0"
postcss-opacity-percentage: "npm:^3.0.0" postcss-opacity-percentage: "npm:^3.0.0"
@ -13996,7 +14023,7 @@ __metadata:
postcss-selector-not: "npm:^8.0.0" postcss-selector-not: "npm:^8.0.0"
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
checksum: 10c0/01660acf3b9ddf4d612a31819e9a5de9fe5383e9eddd2c130180f66ae90c5a881eb408e73454fd50e1839eae71678d738bf72073de08f9013c183b0bd9950fe5 checksum: 10c0/f789000e0504fd827e854bb0feb8b4c218d381314e4d863c5a36df925df412d0844c912952fe27892a320433640aeaff03ee94a3057b42011bf5d32b3963f333
languageName: node languageName: node
linkType: hard linkType: hard
@ -14050,12 +14077,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-safe-parser@npm:^7.0.0": "postcss-safe-parser@npm:^7.0.1":
version: 7.0.0 version: 7.0.1
resolution: "postcss-safe-parser@npm:7.0.0" resolution: "postcss-safe-parser@npm:7.0.1"
peerDependencies: peerDependencies:
postcss: ^8.4.31 postcss: ^8.4.31
checksum: 10c0/4217afd8ce2809e959dc365e4675f499303cc6b91f94db06c8164422822db2d3b3124df701ee2234db4127ad05619b016bfb9c2bccae9bf9cf898a396f1632c9 checksum: 10c0/6957b10b818bd8d4664ec0e548af967f7549abedfb37f844d389571d36af681340f41f9477b9ccf34bcc7599bdef222d1d72e79c64373001fae77089fba6d965
languageName: node languageName: node
linkType: hard linkType: hard
@ -14119,7 +14146,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.41": "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.47":
version: 8.4.47 version: 8.4.47
resolution: "postcss@npm:8.4.47" resolution: "postcss@npm:8.4.47"
dependencies: dependencies:
@ -16717,8 +16744,8 @@ __metadata:
linkType: hard linkType: hard
"stylelint@npm:^16.0.2": "stylelint@npm:^16.0.2":
version: 16.9.0 version: 16.10.0
resolution: "stylelint@npm:16.9.0" resolution: "stylelint@npm:16.10.0"
dependencies: dependencies:
"@csstools/css-parser-algorithms": "npm:^3.0.1" "@csstools/css-parser-algorithms": "npm:^3.0.1"
"@csstools/css-tokenizer": "npm:^3.0.1" "@csstools/css-tokenizer": "npm:^3.0.1"
@ -16728,17 +16755,17 @@ __metadata:
balanced-match: "npm:^2.0.0" balanced-match: "npm:^2.0.0"
colord: "npm:^2.9.3" colord: "npm:^2.9.3"
cosmiconfig: "npm:^9.0.0" cosmiconfig: "npm:^9.0.0"
css-functions-list: "npm:^3.2.2" css-functions-list: "npm:^3.2.3"
css-tree: "npm:^2.3.1" css-tree: "npm:^3.0.0"
debug: "npm:^4.3.6" debug: "npm:^4.3.7"
fast-glob: "npm:^3.3.2" fast-glob: "npm:^3.3.2"
fastest-levenshtein: "npm:^1.0.16" fastest-levenshtein: "npm:^1.0.16"
file-entry-cache: "npm:^9.0.0" file-entry-cache: "npm:^9.1.0"
global-modules: "npm:^2.0.0" global-modules: "npm:^2.0.0"
globby: "npm:^11.1.0" globby: "npm:^11.1.0"
globjoin: "npm:^0.1.4" globjoin: "npm:^0.1.4"
html-tags: "npm:^3.3.1" html-tags: "npm:^3.3.1"
ignore: "npm:^5.3.2" ignore: "npm:^6.0.2"
imurmurhash: "npm:^0.1.4" imurmurhash: "npm:^0.1.4"
is-plain-object: "npm:^5.0.0" is-plain-object: "npm:^5.0.0"
known-css-properties: "npm:^0.34.0" known-css-properties: "npm:^0.34.0"
@ -16747,21 +16774,20 @@ __metadata:
micromatch: "npm:^4.0.8" micromatch: "npm:^4.0.8"
normalize-path: "npm:^3.0.0" normalize-path: "npm:^3.0.0"
picocolors: "npm:^1.0.1" picocolors: "npm:^1.0.1"
postcss: "npm:^8.4.41" postcss: "npm:^8.4.47"
postcss-resolve-nested-selector: "npm:^0.1.6" postcss-resolve-nested-selector: "npm:^0.1.6"
postcss-safe-parser: "npm:^7.0.0" postcss-safe-parser: "npm:^7.0.1"
postcss-selector-parser: "npm:^6.1.2" postcss-selector-parser: "npm:^6.1.2"
postcss-value-parser: "npm:^4.2.0" postcss-value-parser: "npm:^4.2.0"
resolve-from: "npm:^5.0.0" resolve-from: "npm:^5.0.0"
string-width: "npm:^4.2.3" string-width: "npm:^4.2.3"
strip-ansi: "npm:^7.1.0"
supports-hyperlinks: "npm:^3.1.0" supports-hyperlinks: "npm:^3.1.0"
svg-tags: "npm:^1.0.0" svg-tags: "npm:^1.0.0"
table: "npm:^6.8.2" table: "npm:^6.8.2"
write-file-atomic: "npm:^5.0.1" write-file-atomic: "npm:^5.0.1"
bin: bin:
stylelint: bin/stylelint.mjs stylelint: bin/stylelint.mjs
checksum: 10c0/d3ff9c8945c56b04a2fa16ec33d163325496d5db94b6fcb5adf74c76f7f794ac992888273f9a3317652ba8b6195168b2ffff382ca2a667a241e2ace8c9505ae2 checksum: 10c0/d07dd156c225d16c740995daacd78090f7fc317602e87bda2fca323a4ae427a8526d724f3089df3b2185df4520f987547668ceea9b30985988ccbc514034aa21
languageName: node languageName: node
linkType: hard linkType: hard
@ -17406,22 +17432,22 @@ __metadata:
linkType: hard linkType: hard
"typescript@npm:5, typescript@npm:^5.0.4": "typescript@npm:5, typescript@npm:^5.0.4":
version: 5.6.2 version: 5.6.3
resolution: "typescript@npm:5.6.2" resolution: "typescript@npm:5.6.3"
bin: bin:
tsc: bin/tsc tsc: bin/tsc
tsserver: bin/tsserver tsserver: bin/tsserver
checksum: 10c0/3ed8297a8c7c56b7fec282532503d1ac795239d06e7c4966b42d4330c6cf433a170b53bcf93a130a7f14ccc5235de5560df4f1045eb7f3550b46ebed16d3c5e5 checksum: 10c0/44f61d3fb15c35359bc60399cb8127c30bae554cd555b8e2b46d68fa79d680354b83320ad419ff1b81a0bdf324197b29affe6cc28988cd6a74d4ac60c94f9799
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>": "typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
version: 5.6.2 version: 5.6.3
resolution: "typescript@patch:typescript@npm%3A5.6.2#optional!builtin<compat/typescript>::version=5.6.2&hash=8c6c40" resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin<compat/typescript>::version=5.6.3&hash=8c6c40"
bin: bin:
tsc: bin/tsc tsc: bin/tsc
tsserver: bin/tsserver tsserver: bin/tsserver
checksum: 10c0/94eb47e130d3edd964b76da85975601dcb3604b0c848a36f63ac448d0104e93819d94c8bdf6b07c00120f2ce9c05256b8b6092d23cf5cf1c6fa911159e4d572f checksum: 10c0/7c9d2e07c81226d60435939618c91ec2ff0b75fbfa106eec3430f0fcf93a584bc6c73176676f532d78c3594fe28a54b36eb40b3d75593071a7ec91301533ace7
languageName: node languageName: node
linkType: hard linkType: hard
@ -17660,11 +17686,11 @@ __metadata:
linkType: hard linkType: hard
"use-debounce@npm:^10.0.0": "use-debounce@npm:^10.0.0":
version: 10.0.3 version: 10.0.4
resolution: "use-debounce@npm:10.0.3" resolution: "use-debounce@npm:10.0.4"
peerDependencies: peerDependencies:
react: "*" react: "*"
checksum: 10c0/351b62c565d6dce5a21ecc21fe3e1f8db74f70c81c8f7d9dbdfc2da1cb82d883578589f6146e684d91dac534bc3c8b145ab1a36fbf4d44cbb4113827508b39aa checksum: 10c0/73494fc44b2bd58a7ec799a528fc20077c45fe2e94fedff6dcd88d136f7a39f417d77f584d5613aac615ed32aeb2ea393797ae1f7d5b2645eab57cb497a6d0cb
languageName: node languageName: node
linkType: hard linkType: hard