mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2025-01-18 13:54:05 +01:00
Merge branch 'main' of https://github.com/neatchee/mastodon into newmain-tmp3
# Conflicts: # app/javascript/flavours/glitch/styles/components/announcements.scss # config/initializers/content_security_policy.rb
This commit is contained in:
commit
0c34280309
952 changed files with 18316 additions and 17104 deletions
6
.bundler-audit.yml
Normal file
6
.bundler-audit.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
ignore:
|
||||
# devise-two-factor advisory about brute-forcing TOTP
|
||||
# We have rate-limits on authentication endpoints in place (including second
|
||||
# factor verification) since Mastodon v3.2.0
|
||||
- CVE-2024-0227
|
|
@ -5,7 +5,7 @@
|
|||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/sshd:1": {}
|
||||
"ghcr.io/devcontainers/features/sshd:1": {},
|
||||
},
|
||||
|
||||
"runServices": ["app", "db", "redis"],
|
||||
|
@ -15,16 +15,16 @@
|
|||
"portsAttributes": {
|
||||
"3000": {
|
||||
"label": "web",
|
||||
"onAutoForward": "notify"
|
||||
"onAutoForward": "notify",
|
||||
},
|
||||
"4000": {
|
||||
"label": "stream",
|
||||
"onAutoForward": "silent"
|
||||
}
|
||||
"onAutoForward": "silent",
|
||||
},
|
||||
},
|
||||
|
||||
"otherPortsAttributes": {
|
||||
"onAutoForward": "silent"
|
||||
"onAutoForward": "silent",
|
||||
},
|
||||
|
||||
"remoteEnv": {
|
||||
|
@ -33,7 +33,7 @@
|
|||
"STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev",
|
||||
"DISABLE_FORGERY_REQUEST_PROTECTION": "true",
|
||||
"ES_ENABLED": "",
|
||||
"LIBRE_TRANSLATE_ENDPOINT": ""
|
||||
"LIBRE_TRANSLATE_ENDPOINT": "",
|
||||
},
|
||||
|
||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
|
||||
}
|
||||
}
|
||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/sshd:1": {}
|
||||
"ghcr.io/devcontainers/features/sshd:1": {},
|
||||
},
|
||||
|
||||
"forwardPorts": [3000, 4000],
|
||||
|
@ -14,17 +14,17 @@
|
|||
"3000": {
|
||||
"label": "web",
|
||||
"onAutoForward": "notify",
|
||||
"requireLocalPort": true
|
||||
"requireLocalPort": true,
|
||||
},
|
||||
"4000": {
|
||||
"label": "stream",
|
||||
"onAutoForward": "silent",
|
||||
"requireLocalPort": true
|
||||
}
|
||||
"requireLocalPort": true,
|
||||
},
|
||||
},
|
||||
|
||||
"otherPortsAttributes": {
|
||||
"onAutoForward": "silent"
|
||||
"onAutoForward": "silent",
|
||||
},
|
||||
|
||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
|
||||
}
|
||||
}
|
||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ services:
|
|||
hard: -1
|
||||
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:v1.5.3
|
||||
image: libretranslate/libretranslate:v1.5.4
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- lt-data:/home/libretranslate/.local
|
||||
|
|
|
@ -251,6 +251,11 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||
# Maximum allowed character count
|
||||
MAX_TOOT_CHARS=500
|
||||
|
||||
# Maximum allowed hashtags to follow in a feed column
|
||||
# Note that setting this value higher may cause significant
|
||||
# database load
|
||||
MAX_FEED_HASHTAGS=4
|
||||
|
||||
# Maximum number of pinned posts
|
||||
MAX_PINNED_TOOTS=5
|
||||
|
||||
|
|
61
.github/workflows/build-security.yml
vendored
Normal file
61
.github/workflows/build-security.yml
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
name: Build security nightly container image
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
compute-suffix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: version_vars
|
||||
env:
|
||||
TZ: Etc/UTC
|
||||
run: |
|
||||
echo mastodon_version_prerelease=nightly.$(date --date='next day' +'%Y-%m-%d')-security>> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
prerelease: ${{ steps.version_vars.outputs.mastodon_version_prerelease }}
|
||||
|
||||
build-image:
|
||||
needs: compute-suffix
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
use_native_arm64_builder: false
|
||||
cache: false
|
||||
push_to_images: |
|
||||
ghcr.io/${{ github.repository_owner }}/mastodon
|
||||
version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }}
|
||||
labels: |
|
||||
org.opencontainers.image.description=Nightly build image used for testing purposes
|
||||
flavor: |
|
||||
latest=true
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
type=raw,value=nightly
|
||||
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
secrets: inherit
|
||||
|
||||
build-image-streaming:
|
||||
needs: compute-suffix
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: streaming/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
use_native_arm64_builder: false
|
||||
cache: false
|
||||
push_to_images: |
|
||||
ghcr.io/${{ github.repository_owner }}/mastodon-streaming
|
||||
version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }}
|
||||
labels: |
|
||||
org.opencontainers.image.description=Nightly build image used for testing purposes
|
||||
flavor: |
|
||||
latest=true
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
type=raw,value=nightly
|
||||
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
secrets: inherit
|
19
.github/workflows/test-migrations-one-step.yml
vendored
19
.github/workflows/test-migrations-one-step.yml
vendored
|
@ -78,23 +78,8 @@ jobs:
|
|||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
||||
- name: Run migrations up to v2.0.0
|
||||
run: './bin/rails db:migrate VERSION=20171010025614'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2'
|
||||
|
||||
- name: Run migrations up to v2.4.0
|
||||
run: './bin/rails db:migrate VERSION=20180514140000'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4'
|
||||
|
||||
- name: Run migrations up to v2.4.3
|
||||
run: './bin/rails db:migrate VERSION=20180707154237'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4_3'
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
|
||||
- name: Run all remaining migrations
|
||||
run: './bin/rails db:migrate'
|
||||
|
|
22
.github/workflows/test-migrations-two-step.yml
vendored
22
.github/workflows/test-migrations-two-step.yml
vendored
|
@ -45,6 +45,7 @@ jobs:
|
|||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
|
@ -77,28 +78,11 @@ jobs:
|
|||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
||||
- name: Run migrations up to v2.0.0
|
||||
run: './bin/rails db:migrate VERSION=20171010025614'
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2'
|
||||
|
||||
- name: Run pre-deployment migrations up to v2.4.0
|
||||
run: './bin/rails db:migrate VERSION=20180514140000'
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4'
|
||||
|
||||
- name: Run migrations up to v2.4.3
|
||||
run: './bin/rails db:migrate VERSION=20180707154237'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Populate database with test data
|
||||
run: './bin/rails tests:migrations:populate_v2_4_3'
|
||||
|
||||
- name: Run all remaining pre-deployment migrations
|
||||
run: './bin/rails db:migrate'
|
||||
env:
|
||||
|
|
16
.github/workflows/test-ruby.yml
vendored
16
.github/workflows/test-ruby.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
|||
run: |
|
||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.mode == 'test'
|
||||
with:
|
||||
path: |-
|
||||
|
@ -117,7 +117,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -193,7 +193,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -213,14 +213,14 @@ jobs:
|
|||
- run: bundle exec rake spec:system
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-screenshots
|
||||
|
@ -297,7 +297,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
@ -317,14 +317,14 @@ jobs:
|
|||
- run: bin/rspec --tag search
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-screenshots
|
||||
|
|
21
.rubocop.yml
21
.rubocop.yml
|
@ -103,9 +103,26 @@ Rails/Exit:
|
|||
- 'config/boot.rb'
|
||||
- 'lib/mastodon/cli/*.rb'
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
|
||||
Rails/LexicallyScopedActionFilter:
|
||||
Exclude:
|
||||
- 'db/*migrate/**/*'
|
||||
- 'app/controllers/auth/*'
|
||||
|
||||
# Reason: These tasks are doing local work which do not need full env loaded
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsrakeenvironment
|
||||
Rails/RakeEnvironment:
|
||||
Exclude:
|
||||
- 'lib/tasks/auto_annotate_models.rake'
|
||||
- 'lib/tasks/emojis.rake'
|
||||
- 'lib/tasks/mastodon.rake'
|
||||
- 'lib/tasks/repo.rake'
|
||||
- 'lib/tasks/statistics.rake'
|
||||
|
||||
# Reason: There are appropriate times to use these features
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsskipsmodelvalidations
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
# Reason: We want to preserve the ability to migrate from arbitrary old versions,
|
||||
# and cannot guarantee that every installation has run every migration as they upgrade.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.59.0.
|
||||
# using RuboCop version 1.60.2.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
|
@ -13,13 +13,6 @@ Bundler/OrderedGems:
|
|||
Exclude:
|
||||
- 'Gemfile'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
||||
# URISchemes: http, https
|
||||
Layout/LineLength:
|
||||
Exclude:
|
||||
- 'app/models/account.rb'
|
||||
|
||||
Lint/NonLocalExitFromIterator:
|
||||
Exclude:
|
||||
- 'app/helpers/jsonld_helper.rb'
|
||||
|
@ -74,64 +67,10 @@ Rails/HasAndBelongsToMany:
|
|||
- 'app/models/status.rb'
|
||||
- 'app/models/tag.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
|
||||
Rails/LexicallyScopedActionFilter:
|
||||
Exclude:
|
||||
- 'app/controllers/auth/passwords_controller.rb'
|
||||
- 'app/controllers/auth/registrations_controller.rb'
|
||||
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'config/initializers/simple_form.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: Include.
|
||||
# Include: **/Rakefile, **/*.rake
|
||||
Rails/RakeEnvironment:
|
||||
Exclude:
|
||||
- 'lib/tasks/auto_annotate_models.rake'
|
||||
- 'lib/tasks/db.rake'
|
||||
- 'lib/tasks/emojis.rake'
|
||||
- 'lib/tasks/mastodon.rake'
|
||||
- 'lib/tasks/repo.rake'
|
||||
- 'lib/tasks/statistics.rake'
|
||||
|
||||
# Configuration parameters: ForbiddenMethods, AllowedMethods.
|
||||
# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
|
||||
Rails/SkipsModelValidations:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/invites_controller.rb'
|
||||
- 'app/controllers/concerns/session_tracking_concern.rb'
|
||||
- 'app/models/concerns/account/merging.rb'
|
||||
- 'app/models/concerns/expireable.rb'
|
||||
- 'app/models/status.rb'
|
||||
- 'app/models/trends/links.rb'
|
||||
- 'app/models/trends/preview_card_batch.rb'
|
||||
- 'app/models/trends/preview_card_provider_batch.rb'
|
||||
- 'app/models/trends/status_batch.rb'
|
||||
- 'app/models/trends/statuses.rb'
|
||||
- 'app/models/trends/tag_batch.rb'
|
||||
- 'app/models/trends/tags.rb'
|
||||
- 'app/models/user.rb'
|
||||
- 'app/services/activitypub/process_status_update_service.rb'
|
||||
- 'app/services/approve_appeal_service.rb'
|
||||
- 'app/services/block_domain_service.rb'
|
||||
- 'app/services/delete_account_service.rb'
|
||||
- 'app/services/process_mentions_service.rb'
|
||||
- 'app/services/unallow_domain_service.rb'
|
||||
- 'app/services/unblock_domain_service.rb'
|
||||
- 'app/services/update_status_service.rb'
|
||||
- 'app/workers/activitypub/post_upgrade_worker.rb'
|
||||
- 'app/workers/move_worker.rb'
|
||||
- 'app/workers/scheduler/ip_cleanup_scheduler.rb'
|
||||
- 'app/workers/scheduler/scheduled_statuses_scheduler.rb'
|
||||
- 'lib/mastodon/cli/accounts.rb'
|
||||
- 'lib/mastodon/cli/maintenance.rb'
|
||||
- 'spec/lib/activitypub/activity/follow_spec.rb'
|
||||
- 'spec/services/follow_service_spec.rb'
|
||||
- 'spec/services/update_account_service_spec.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/UniqueValidationWithoutIndex:
|
||||
|
@ -141,38 +80,6 @@ Rails/UniqueValidationWithoutIndex:
|
|||
- 'app/models/identity.rb'
|
||||
- 'app/models/webauthn_credential.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: exists, where
|
||||
Rails/WhereExists:
|
||||
Exclude:
|
||||
- 'app/controllers/activitypub/inboxes_controller.rb'
|
||||
- 'app/controllers/admin/email_domain_blocks_controller.rb'
|
||||
- 'app/lib/activitypub/activity/create.rb'
|
||||
- 'app/lib/delivery_failure_tracker.rb'
|
||||
- 'app/lib/feed_manager.rb'
|
||||
- 'app/lib/status_cache_hydrator.rb'
|
||||
- 'app/lib/suspicious_sign_in_detector.rb'
|
||||
- 'app/models/concerns/account/interactions.rb'
|
||||
- 'app/models/featured_tag.rb'
|
||||
- 'app/models/poll.rb'
|
||||
- 'app/models/session_activation.rb'
|
||||
- 'app/models/status.rb'
|
||||
- 'app/models/user.rb'
|
||||
- 'app/policies/status_policy.rb'
|
||||
- 'app/serializers/rest/announcement_serializer.rb'
|
||||
- 'app/serializers/rest/tag_serializer.rb'
|
||||
- 'app/services/activitypub/fetch_remote_status_service.rb'
|
||||
- 'app/services/vote_service.rb'
|
||||
- 'app/validators/reaction_validator.rb'
|
||||
- 'app/validators/vote_validator.rb'
|
||||
- 'app/workers/move_worker.rb'
|
||||
- 'lib/tasks/tests.rake'
|
||||
- 'spec/models/account_spec.rb'
|
||||
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
||||
- 'spec/services/purge_domain_service_spec.rb'
|
||||
- 'spec/services/unallow_domain_service_spec.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
# AllowedMethods: ==, equal?, eql?
|
||||
|
@ -211,7 +118,6 @@ Style/FetchEnvVar:
|
|||
# AllowedMethods: redirect
|
||||
Style/FormatStringToken:
|
||||
Exclude:
|
||||
- 'app/models/privacy_policy.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'lib/paperclip/color_extractor.rb'
|
||||
|
||||
|
@ -629,10 +535,6 @@ Style/GlobalStdStream:
|
|||
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
||||
Style/GuardClause:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/confirmations_controller.rb'
|
||||
- 'app/controllers/auth/confirmations_controller.rb'
|
||||
- 'app/controllers/auth/passwords_controller.rb'
|
||||
- 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
|
||||
- 'app/lib/activitypub/activity/block.rb'
|
||||
- 'app/lib/request.rb'
|
||||
- 'app/lib/request_pool.rb'
|
||||
|
@ -794,13 +696,6 @@ Style/StringLiterals:
|
|||
- 'config/routes.rb'
|
||||
- 'spec/fabricators/status_reaction_fabricator.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
||||
Style/TernaryParentheses:
|
||||
Exclude:
|
||||
- 'config/environments/development.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyleForMultiline.
|
||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.2.2
|
||||
3.2.3
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
||||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.2"]
|
||||
ARG RUBY_VERSION="3.2.2"
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.3"]
|
||||
ARG RUBY_VERSION="3.2.3"
|
||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||
ARG NODE_MAJOR_VERSION="20"
|
||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||
ARG DEBIAN_VERSION="bookworm"
|
||||
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.2.2-slim-bookworm)
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.2.3-slim-bookworm)
|
||||
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
|
||||
|
||||
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
## ActivityPub federation in Mastodon
|
||||
# Federation
|
||||
|
||||
## Supported federation protocols and standards
|
||||
|
||||
- [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server)
|
||||
- [WebFinger](https://webfinger.net/)
|
||||
- [Http Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
|
||||
- [NodeInfo](https://nodeinfo.diaspora.software/)
|
||||
|
||||
## Supported FEPs
|
||||
|
||||
- [FEP-67ff: FEDERATION.md](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md)
|
||||
- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md)
|
||||
- [FEP-8fcf: Followers collection synchronization across servers](https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md)
|
||||
- [FEP-5feb: Search indexing consent for actors](https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md)
|
||||
|
||||
## ActivityPub in Mastodon
|
||||
|
||||
Mastodon largely follows the ActivityPub server-to-server specification but it makes uses of some non-standard extensions, some of which are required for interacting with Mastodon at all.
|
||||
|
||||
Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/
|
||||
- [Supported ActivityPub vocabulary](https://docs.joinmastodon.org/spec/activitypub/)
|
||||
|
||||
### Required extensions
|
||||
|
||||
#### Webfinger
|
||||
#### WebFinger
|
||||
|
||||
In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`).
|
||||
This is used both for discovery and for unambiguously mentioning users across the fediverse. Furthermore, this is part of Mastodon's database design from its very beginnings.
|
||||
|
||||
As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger.
|
||||
|
||||
More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/
|
||||
- [WebFinger information and examples](https://docs.joinmastodon.org/spec/webfinger/)
|
||||
|
||||
#### HTTP Signatures
|
||||
|
||||
|
@ -21,11 +37,13 @@ In order to authenticate activities, Mastodon relies on HTTP Signatures, signing
|
|||
|
||||
Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server.
|
||||
|
||||
More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http
|
||||
- [HTTP Signatures information and examples](https://docs.joinmastodon.org/spec/security/#http)
|
||||
|
||||
### Optional extensions
|
||||
|
||||
- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
|
||||
- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
|
||||
- Followers collection synchronization: https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
|
||||
- Search indexing consent for actors: https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md
|
||||
- [Linked-Data Signatures](https://docs.joinmastodon.org/spec/security/#ld)
|
||||
- [Bearcaps](https://docs.joinmastodon.org/spec/bearcaps/)
|
||||
|
||||
### Additional documentation
|
||||
|
||||
- [Mastodon documentation](https://docs.joinmastodon.org/)
|
||||
|
|
9
Gemfile
9
Gemfile
|
@ -26,7 +26,7 @@ gem 'blurhash', '~> 0.1'
|
|||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', '~> 1.17.0', require: false
|
||||
gem 'bootsnap', '~> 1.18.0', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy', '~> 7.3'
|
||||
|
@ -39,8 +39,7 @@ end
|
|||
|
||||
gem 'net-ldap', '~> 0.18'
|
||||
|
||||
# TODO: Point back at released omniauth-cas gem when new version is released
|
||||
gem 'omniauth-cas', github: 'dlindahl/omniauth-cas', ref: '9d9d3a91b316c55d49ab6e621977f2067010c5bf'
|
||||
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
||||
gem 'omniauth-saml', '~> 2.0'
|
||||
gem 'omniauth_openid_connect', '~> 0.6.1'
|
||||
gem 'omniauth', '~> 2.0'
|
||||
|
@ -64,7 +63,7 @@ gem 'kaminari', '~> 1.2'
|
|||
gem 'link_header', '~> 0.0'
|
||||
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
|
||||
gem 'nokogiri', '~> 1.15'
|
||||
gem 'nsa', github: 'jhawthorn/nsa', ref: 'e020fcc3a54d993ab45b7194d89ab720296c111b'
|
||||
gem 'nsa'
|
||||
gem 'oj', '~> 3.14'
|
||||
gem 'ox', '~> 2.14'
|
||||
gem 'parslet'
|
||||
|
@ -124,7 +123,7 @@ group :test do
|
|||
gem 'database_cleaner-active_record'
|
||||
|
||||
# Used to mock environment variables
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'climate_control'
|
||||
|
||||
# Generating fake data for specs
|
||||
gem 'faker', '~> 3.2'
|
||||
|
|
220
Gemfile.lock
220
Gemfile.lock
|
@ -7,59 +7,38 @@ GIT
|
|||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/dlindahl/omniauth-cas.git
|
||||
revision: 9d9d3a91b316c55d49ab6e621977f2067010c5bf
|
||||
ref: 9d9d3a91b316c55d49ab6e621977f2067010c5bf
|
||||
specs:
|
||||
omniauth-cas (3.0.0)
|
||||
addressable (~> 2.8)
|
||||
nokogiri (~> 1.12)
|
||||
omniauth (~> 2.1)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/jhawthorn/nsa.git
|
||||
revision: e020fcc3a54d993ab45b7194d89ab720296c111b
|
||||
ref: e020fcc3a54d993ab45b7194d89ab720296c111b
|
||||
specs:
|
||||
nsa (0.2.8)
|
||||
activesupport (>= 4.2, < 7.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actioncable (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actionmailbox (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
activejob (= 7.1.3)
|
||||
activerecord (= 7.1.3)
|
||||
activestorage (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actionmailer (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
actionview (= 7.1.3)
|
||||
activejob (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actionpack (7.1.3)
|
||||
actionview (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
|
@ -67,15 +46,15 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actiontext (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
activerecord (= 7.1.3)
|
||||
activestorage (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
actionview (7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
|
@ -85,22 +64,22 @@ GEM
|
|||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
activejob (7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
activerecord (7.1.2)
|
||||
activemodel (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
activemodel (7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
activerecord (7.1.3)
|
||||
activemodel (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
activestorage (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
activejob (= 7.1.3)
|
||||
activerecord (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
marcel (~> 1.0)
|
||||
activesupport (7.1.2)
|
||||
activesupport (7.1.3)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
|
@ -160,14 +139,14 @@ GEM
|
|||
erubi (~> 1.4)
|
||||
parser (>= 2.4)
|
||||
smart_properties
|
||||
bigdecimal (3.1.5)
|
||||
bigdecimal (3.1.6)
|
||||
bindata (2.4.15)
|
||||
binding_of_caller (1.0.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blurhash (0.1.7)
|
||||
bootsnap (1.17.0)
|
||||
bootsnap (1.18.3)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (6.1.1)
|
||||
brakeman (6.1.2)
|
||||
racc
|
||||
browser (5.3.1)
|
||||
brpoplpush-redis_script (0.1.3)
|
||||
|
@ -177,11 +156,11 @@ GEM
|
|||
bundler-audit (0.9.1)
|
||||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 1.0)
|
||||
capybara (3.39.2)
|
||||
capybara (3.40.0)
|
||||
addressable
|
||||
matrix
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
nokogiri (~> 1.11)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
|
@ -190,20 +169,21 @@ GEM
|
|||
activesupport
|
||||
cbor (0.5.9.6)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (7.4.0)
|
||||
chewy (7.5.1)
|
||||
activesupport (>= 5.2)
|
||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.4.0)
|
||||
climate_control (0.2.0)
|
||||
climate_control (1.2.0)
|
||||
cocoon (1.2.15)
|
||||
color_diff (0.1)
|
||||
concurrent-ruby (1.2.2)
|
||||
concurrent-ruby (1.2.3)
|
||||
connection_pool (2.4.1)
|
||||
cose (1.3.0)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 1.0)
|
||||
crack (0.4.5)
|
||||
crack (0.4.6)
|
||||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.14.0)
|
||||
|
@ -267,7 +247,7 @@ GEM
|
|||
tzinfo
|
||||
excon (0.109.0)
|
||||
fabrication (2.31.0)
|
||||
faker (3.2.2)
|
||||
faker (3.2.3)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
|
@ -329,13 +309,13 @@ GEM
|
|||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
haml_lint (0.53.0)
|
||||
haml_lint (0.56.0)
|
||||
haml (>= 5.0)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
rubocop (>= 1.0)
|
||||
sysexits (~> 1.1)
|
||||
hashdiff (1.0.1)
|
||||
hashdiff (1.1.0)
|
||||
hashie (5.0.0)
|
||||
hcaptcha (7.1.0)
|
||||
json
|
||||
|
@ -370,8 +350,8 @@ GEM
|
|||
rainbow (>= 2.2.2, < 4.0)
|
||||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.7.1)
|
||||
irb (1.11.1)
|
||||
io-console (0.7.2)
|
||||
irb (1.11.2)
|
||||
rdoc
|
||||
reline (>= 0.4.2)
|
||||
jmespath (1.6.2)
|
||||
|
@ -408,12 +388,12 @@ GEM
|
|||
activerecord
|
||||
kaminari-core (= 1.2.2)
|
||||
kaminari-core (1.2.2)
|
||||
kt-paperclip (7.2.1)
|
||||
kt-paperclip (7.2.2)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
marcel (~> 1.0.1)
|
||||
mime-types
|
||||
terrapin (~> 0.6.0)
|
||||
terrapin (>= 0.6.0, < 2.0)
|
||||
language_server-protocol (3.17.0.3)
|
||||
launchy (2.5.2)
|
||||
addressable (~> 2.8)
|
||||
|
@ -455,7 +435,7 @@ GEM
|
|||
mime-types-data (3.2023.1205)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.5)
|
||||
minitest (5.20.0)
|
||||
minitest (5.21.2)
|
||||
msgpack (1.7.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.3.0)
|
||||
|
@ -464,7 +444,7 @@ GEM
|
|||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.4)
|
||||
net-imap (0.4.9.1)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.19.0)
|
||||
|
@ -472,18 +452,27 @@ GEM
|
|||
net-protocol
|
||||
net-protocol (0.2.2)
|
||||
timeout
|
||||
net-smtp (0.4.0)
|
||||
net-smtp (0.4.0.1)
|
||||
net-protocol
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.16.0)
|
||||
nokogiri (1.16.2)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nsa (0.3.0)
|
||||
activesupport (>= 4.2, < 7.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
oj (3.16.3)
|
||||
bigdecimal (>= 3.0)
|
||||
omniauth (2.1.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 2.2.3)
|
||||
rack-protection
|
||||
omniauth-cas (3.0.0.beta.1)
|
||||
addressable (~> 2.8)
|
||||
nokogiri (~> 1.12)
|
||||
omniauth (~> 2.1)
|
||||
omniauth-rails_csrf_protection (1.0.1)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (~> 2.0)
|
||||
|
@ -510,14 +499,14 @@ GEM
|
|||
orm_adapter (0.5.0)
|
||||
ox (2.14.17)
|
||||
parallel (1.24.0)
|
||||
parser (3.2.2.4)
|
||||
parser (3.3.0.5)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
parslet (2.0.0)
|
||||
pastel (0.8.0)
|
||||
tty-color (~> 0.5)
|
||||
pg (1.5.4)
|
||||
pghero (3.4.0)
|
||||
pghero (3.4.1)
|
||||
activerecord (>= 6)
|
||||
posix-spawn (0.3.15)
|
||||
premailer (1.21.0)
|
||||
|
@ -558,27 +547,27 @@ GEM
|
|||
rack
|
||||
rack-proxy (0.7.6)
|
||||
rack
|
||||
rack-session (1.0.1)
|
||||
rack-session (1.0.2)
|
||||
rack (< 3)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rackup (1.0.0)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.2)
|
||||
actioncable (= 7.1.2)
|
||||
actionmailbox (= 7.1.2)
|
||||
actionmailer (= 7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
actiontext (= 7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activemodel (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
rails (7.1.3)
|
||||
actioncable (= 7.1.3)
|
||||
actionmailbox (= 7.1.3)
|
||||
actionmailer (= 7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
actiontext (= 7.1.3)
|
||||
actionview (= 7.1.3)
|
||||
activejob (= 7.1.3)
|
||||
activemodel (= 7.1.3)
|
||||
activerecord (= 7.1.3)
|
||||
activestorage (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.1.2)
|
||||
railties (= 7.1.3)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -593,9 +582,9 @@ GEM
|
|||
rails-i18n (7.0.8)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
railties (7.1.3)
|
||||
actionpack (= 7.1.3)
|
||||
activesupport (= 7.1.3)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
@ -606,8 +595,8 @@ GEM
|
|||
rdf (3.3.1)
|
||||
bcp47_spec (~> 0.2)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.6.1)
|
||||
rdf (~> 3.2)
|
||||
rdf-normalize (0.7.0)
|
||||
rdf (~> 3.3)
|
||||
rdoc (6.6.2)
|
||||
psych (>= 4.0.0)
|
||||
redcarpet (3.6.0)
|
||||
|
@ -616,7 +605,7 @@ GEM
|
|||
redis (>= 4)
|
||||
redlock (1.3.2)
|
||||
redis (>= 3.0.0, < 6.0)
|
||||
regexp_parser (2.8.3)
|
||||
regexp_parser (2.9.0)
|
||||
reline (0.4.2)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
|
@ -642,7 +631,7 @@ GEM
|
|||
rspec-mocks (3.12.6)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.1.0)
|
||||
rspec-rails (6.1.1)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
|
@ -656,11 +645,11 @@ GEM
|
|||
rspec-mocks (~> 3.0)
|
||||
sidekiq (>= 5, < 8)
|
||||
rspec-support (3.12.1)
|
||||
rubocop (1.59.0)
|
||||
rubocop (1.60.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.4)
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
|
@ -702,7 +691,8 @@ GEM
|
|||
scenic (1.7.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
selenium-webdriver (4.16.0)
|
||||
selenium-webdriver (4.17.0)
|
||||
base64 (~> 0.2)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
|
@ -717,7 +707,7 @@ GEM
|
|||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 6, < 8)
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (7.1.31)
|
||||
sidekiq-unique-jobs (7.1.33)
|
||||
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
redis (< 5.0)
|
||||
|
@ -736,7 +726,7 @@ GEM
|
|||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
smart_properties (1.17.0)
|
||||
stackprof (0.2.25)
|
||||
stackprof (0.2.26)
|
||||
statsd-ruby (1.5.0)
|
||||
stoplight (3.0.2)
|
||||
redlock (~> 1.0)
|
||||
|
@ -751,8 +741,8 @@ GEM
|
|||
temple (0.10.3)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
terrapin (0.6.0)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
terrapin (1.0.1)
|
||||
climate_control
|
||||
test-prof (1.3.1)
|
||||
thor (1.3.0)
|
||||
tilt (2.3.0)
|
||||
|
@ -776,7 +766,7 @@ GEM
|
|||
unf (~> 0.1.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2023.4)
|
||||
tzinfo-data (1.2024.1)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
|
@ -803,7 +793,7 @@ GEM
|
|||
webfinger (1.2.0)
|
||||
activesupport
|
||||
httpclient (>= 2.4)
|
||||
webmock (3.19.1)
|
||||
webmock (3.20.0)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
@ -834,14 +824,14 @@ DEPENDENCIES
|
|||
better_errors (~> 2.9)
|
||||
binding_of_caller (~> 1.0)
|
||||
blurhash (~> 0.1)
|
||||
bootsnap (~> 1.17.0)
|
||||
bootsnap (~> 1.18.0)
|
||||
brakeman (~> 6.0)
|
||||
browser
|
||||
bundler-audit (~> 0.9)
|
||||
capybara (~> 3.39)
|
||||
charlock_holmes (~> 0.7.7)
|
||||
chewy (~> 7.3)
|
||||
climate_control (~> 0.2)
|
||||
climate_control
|
||||
cocoon (~> 1.2)
|
||||
color_diff (~> 0.1)
|
||||
concurrent-ruby
|
||||
|
@ -891,10 +881,10 @@ DEPENDENCIES
|
|||
net-http (~> 0.4.0)
|
||||
net-ldap (~> 0.18)
|
||||
nokogiri (~> 1.15)
|
||||
nsa!
|
||||
nsa
|
||||
oj (~> 3.14)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-cas!
|
||||
omniauth-cas (~> 3.0.0.beta.1)
|
||||
omniauth-rails_csrf_protection (~> 1.0)
|
||||
omniauth-saml (~> 2.0)
|
||||
omniauth_openid_connect (~> 0.6.1)
|
||||
|
@ -961,4 +951,4 @@ RUBY VERSION
|
|||
ruby 3.2.2p53
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.20
|
||||
2.5.4
|
||||
|
|
|
@ -24,7 +24,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
|
|||
end
|
||||
|
||||
def set_items
|
||||
@items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri)
|
||||
@items = @account.followers.matches_uri_prefix(uri_prefix).pluck(:uri)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
|
|
|
@ -24,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
|
||||
def unknown_affected_account?
|
||||
json = Oj.load(body, mode: :strict)
|
||||
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
|
||||
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.exists?(uri: json['actor'])
|
||||
rescue Oj::ParseError
|
||||
false
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Admin
|
|||
|
||||
def index
|
||||
authorize :audit_log, :index?
|
||||
@auditable_accounts = Account.where(id: Admin::ActionLog.select('distinct account_id')).select(:id, :username)
|
||||
@auditable_accounts = Account.auditable.select(:id, :username)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
module Admin
|
||||
class ConfirmationsController < BaseController
|
||||
before_action :set_user
|
||||
before_action :check_confirmation, only: [:resend]
|
||||
before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed?
|
||||
|
||||
def create
|
||||
authorize @user, :confirm?
|
||||
@user.confirm!
|
||||
@user.mark_email_as_confirmed!
|
||||
log_action :confirm, @user
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
@ -25,11 +25,13 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
def check_confirmation
|
||||
if @user.confirmed?
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
def redirect_confirmed_user
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def user_confirmed?
|
||||
@user.confirmed?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ module Admin
|
|||
log_action :create, @email_domain_block
|
||||
|
||||
(@email_domain_block.other_domains || []).uniq.each do |domain|
|
||||
next if EmailDomainBlock.where(domain: domain).exists?
|
||||
next if EmailDomainBlock.exists?(domain: domain)
|
||||
|
||||
other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block)
|
||||
log_action :create, other_email_domain_block
|
||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
|||
next
|
||||
end
|
||||
|
||||
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
|
||||
@warning_domains = instances_from_imported_blocks.pluck(:domain)
|
||||
rescue ActionController::ParameterMissing
|
||||
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
|
||||
set_dummy_import!
|
||||
|
@ -58,6 +58,10 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
def instances_from_imported_blocks
|
||||
Instance.with_domain_follows(@domain_blocks.map(&:domain))
|
||||
end
|
||||
|
||||
def export_filename
|
||||
'domain_blocks.csv'
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
return [] if hide_results?
|
||||
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil? || current_account.id == @account.id
|
||||
scope.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
|
||||
Account.includes(:active_relationships, :account_stat, :user).references(:active_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
return [] if hide_results?
|
||||
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil? || current_account.id == @account.id
|
||||
scope.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
|
||||
Account.includes(:passive_relationships, :account_stat, :user).references(:passive_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
|
|
30
app/controllers/api/v1/annual_reports_controller.rb
Normal file
30
app/controllers/api/v1/annual_reports_controller.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AnnualReportsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
||||
before_action :require_user!
|
||||
before_action :set_annual_report, except: :index
|
||||
|
||||
def index
|
||||
with_read_replica do
|
||||
@presenter = AnnualReportsPresenter.new(GeneratedAnnualReport.where(account_id: current_account.id).pending)
|
||||
@relationships = StatusRelationshipsPresenter.new(@presenter.statuses, current_account.id)
|
||||
end
|
||||
|
||||
render json: @presenter,
|
||||
serializer: REST::AnnualReportsSerializer,
|
||||
relationships: @relationships
|
||||
end
|
||||
|
||||
def read
|
||||
@annual_report.view!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_annual_report
|
||||
@annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: params[:id])
|
||||
end
|
||||
end
|
|
@ -17,7 +17,7 @@ class Api::V1::BlocksController < Api::BaseController
|
|||
end
|
||||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
||||
@paginated_blocks ||= Block.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
|
|
|
@ -27,7 +27,7 @@ class Api::V1::DirectoriesController < Api::BaseController
|
|||
scope.merge!(local_account_scope) if local_accounts?
|
||||
scope.merge!(account_exclusion_scope) if current_account
|
||||
scope.merge!(account_domain_block_scope) if current_account && !local_accounts?
|
||||
end
|
||||
end.includes(:account_stat, user: :role)
|
||||
end
|
||||
|
||||
def local_accounts?
|
||||
|
|
|
@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController
|
|||
end
|
||||
|
||||
def endorsed_accounts
|
||||
current_account.endorsed_accounts.includes(:account_stat).without_suspended
|
||||
current_account.endorsed_accounts.includes(:account_stat, :user).without_suspended
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||
Account.without_suspended.includes(:follow_requests, :account_stat, :user).references(:follow_requests)
|
||||
end
|
||||
|
||||
def paginated_follow_requests
|
||||
|
|
|
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@list.accounts.without_suspended.includes(:account_stat).all
|
||||
@list.accounts.without_suspended.includes(:account_stat, :user).all
|
||||
else
|
||||
@list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
@list.accounts.without_suspended.includes(:account_stat, :user).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class Api::V1::MarkersController < Api::BaseController
|
|||
@markers = {}
|
||||
|
||||
resource_params.each_pair do |timeline, timeline_params|
|
||||
@markers[timeline] = current_user.markers.find_or_initialize_by(timeline: timeline)
|
||||
@markers[timeline] = current_user.markers.find_or_create_by(timeline: timeline)
|
||||
@markers[timeline].update!(timeline_params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ class Api::V1::MutesController < Api::BaseController
|
|||
end
|
||||
|
||||
def paginated_mutes
|
||||
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||
@paginated_mutes ||= Mute.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
|
|
|
@ -27,7 +27,7 @@ class Api::V1::Peers::SearchController < Api::BaseController
|
|||
@domains = InstancesIndex.query(function_score: {
|
||||
query: {
|
||||
prefix: {
|
||||
domain: TagManager.instance.normalize_domain(params[:q].strip),
|
||||
domain: normalized_domain,
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -37,11 +37,18 @@ class Api::V1::Peers::SearchController < Api::BaseController
|
|||
},
|
||||
}).limit(10).pluck(:domain)
|
||||
else
|
||||
domain = params[:q].strip
|
||||
domain = TagManager.instance.normalize_domain(domain)
|
||||
@domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain)
|
||||
domain = normalized_domain
|
||||
@domains = Instance.searchable.domain_starts_with(domain).limit(10).pluck(:domain)
|
||||
end
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
@domains = []
|
||||
end
|
||||
|
||||
def normalized_domain
|
||||
TagManager.instance.normalize_domain(query_value)
|
||||
end
|
||||
|
||||
def query_value
|
||||
params[:q].strip
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,14 +14,14 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::Bas
|
|||
|
||||
def load_accounts
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
||||
scope.merge(paginated_favourites).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account
|
||||
.without_suspended
|
||||
.includes(:favourites, :account_stat)
|
||||
.includes(:favourites, :account_stat, :user)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
end
|
||||
|
|
|
@ -1,38 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::ReactionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::ReactionsController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
def create
|
||||
ReactService.new.call(current_account, @status, normalize(params[:id]))
|
||||
ReactService.new.call(current_account, @status, params[:id])
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
UnreactWorker.perform_async(current_account.id, @status.id, normalize(params[:id]))
|
||||
UnreactWorker.perform_async(current_account.id, @status.id, params[:id])
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reactions_map: { @status.id => false })
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize(name)
|
||||
normalized = "#{name}\uFE0F"
|
||||
return normalized if StatusReactionValidator::SUPPORTED_EMOJIS.include?(normalized)
|
||||
|
||||
name
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,12 +14,12 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::Base
|
|||
|
||||
def load_accounts
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
||||
scope.merge(paginated_statuses).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.without_suspended.includes(:statuses, :account_stat).references(:statuses)
|
||||
Account.without_suspended.includes(:statuses, :account_stat, :user).references(:statuses)
|
||||
end
|
||||
|
||||
def paginated_statuses
|
||||
|
|
|
@ -35,7 +35,7 @@ class Api::V2::FiltersController < Api::BaseController
|
|||
private
|
||||
|
||||
def set_filters
|
||||
@filters = current_account.custom_filters.includes(:keywords)
|
||||
@filters = current_account.custom_filters.includes(:keywords, :statuses)
|
||||
end
|
||||
|
||||
def set_filter
|
||||
|
|
|
@ -8,7 +8,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
before_action :set_body_classes
|
||||
before_action :set_pack
|
||||
before_action :set_confirmation_user!, only: [:show, :confirm_captcha]
|
||||
before_action :require_unconfirmed!
|
||||
before_action :redirect_confirmed_user, if: :signed_in_confirmed_user?
|
||||
|
||||
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
||||
before_action :require_captcha_if_needed!, only: [:show]
|
||||
|
@ -70,10 +70,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
use_pack 'auth'
|
||||
end
|
||||
|
||||
def require_unconfirmed!
|
||||
if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
|
||||
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
|
||||
end
|
||||
def redirect_confirmed_user
|
||||
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
|
||||
end
|
||||
|
||||
def signed_in_confirmed_user?
|
||||
user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class Auth::PasswordsController < Devise::PasswordsController
|
||||
skip_before_action :check_self_destruct!
|
||||
before_action :check_validity_of_reset_password_token, only: :edit
|
||||
before_action :redirect_invalid_reset_token, only: :edit, unless: :reset_password_token_is_valid?
|
||||
before_action :set_pack
|
||||
before_action :set_body_classes
|
||||
|
||||
|
@ -20,11 +20,9 @@ class Auth::PasswordsController < Devise::PasswordsController
|
|||
|
||||
private
|
||||
|
||||
def check_validity_of_reset_password_token
|
||||
unless reset_password_token_is_valid?
|
||||
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
||||
redirect_to new_password_path(resource_name)
|
||||
end
|
||||
def redirect_invalid_reset_token
|
||||
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
||||
redirect_to new_password_path(resource_name)
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Auth::SessionsController < Devise::SessionsController
|
||||
include Redisable
|
||||
|
||||
MAX_2FA_ATTEMPTS_PER_HOUR = 10
|
||||
|
||||
layout 'auth'
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
|
@ -135,9 +139,23 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
session.delete(:attempt_user_updated_at)
|
||||
end
|
||||
|
||||
def clear_2fa_attempt_from_user(user)
|
||||
redis.del(second_factor_attempts_key(user))
|
||||
end
|
||||
|
||||
def check_second_factor_rate_limits(user)
|
||||
attempts, = redis.multi do |multi|
|
||||
multi.incr(second_factor_attempts_key(user))
|
||||
multi.expire(second_factor_attempts_key(user), 1.hour)
|
||||
end
|
||||
|
||||
attempts >= MAX_2FA_ATTEMPTS_PER_HOUR
|
||||
end
|
||||
|
||||
def on_authentication_success(user, security_measure)
|
||||
@on_authentication_success_called = true
|
||||
|
||||
clear_2fa_attempt_from_user(user)
|
||||
clear_attempt_from_session
|
||||
|
||||
user.update_sign_in!(new_sign_in: true)
|
||||
|
@ -168,5 +186,14 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
ip: request.remote_ip,
|
||||
user_agent: request.user_agent
|
||||
)
|
||||
|
||||
# Only send a notification email every hour at most
|
||||
return if redis.set("2fa_failure_notification:#{user.id}", '1', ex: 1.hour, get: true).present?
|
||||
|
||||
UserMailer.failed_2fa(user, request.remote_ip, request.user_agent, Time.now.utc).deliver_later!
|
||||
end
|
||||
|
||||
def second_factor_attempts_key(user)
|
||||
"2fa_auth_attempts:#{user.id}:#{Time.now.utc.hour}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,6 +66,11 @@ module Auth::TwoFactorAuthenticationConcern
|
|||
end
|
||||
|
||||
def authenticate_with_two_factor_via_otp(user)
|
||||
if check_second_factor_rate_limits(user)
|
||||
flash.now[:alert] = I18n.t('users.rate_limited')
|
||||
return prompt_for_two_factor(user)
|
||||
end
|
||||
|
||||
if valid_otp_attempt?(user)
|
||||
on_authentication_success(user, :otp)
|
||||
else
|
||||
|
|
|
@ -266,7 +266,7 @@ module SignatureVerification
|
|||
stoplight_wrap_request { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
||||
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
||||
account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
|
||||
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false, suppress_errors: false) }
|
||||
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
||||
account
|
||||
end
|
||||
rescue Mastodon::PrivateNetworkAddressError => e
|
||||
|
|
|
@ -22,11 +22,20 @@ module WebAppControllerConcern
|
|||
def redirect_unauthenticated_to_permalinks!
|
||||
return if user_signed_in? # NOTE: Different from upstream because we allow moved users to log in
|
||||
|
||||
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||
return if redirect_path.blank?
|
||||
permalink_redirector = PermalinkRedirector.new(request.path)
|
||||
return if permalink_redirector.redirect_path.blank?
|
||||
|
||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
|
||||
redirect_to(redirect_path)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to(permalink_redirector.redirect_confirmation_path, allow_other_host: false)
|
||||
end
|
||||
|
||||
format.json do
|
||||
redirect_to(permalink_redirector.redirect_uri, allow_other_host: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_pack
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
||||
before_action :set_user_roles
|
||||
|
||||
def show
|
||||
expires_in 3.minutes, public: true
|
||||
render content_type: 'text/css'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def custom_css_styles
|
||||
Setting.custom_css
|
||||
end
|
||||
helper_method :custom_css_styles
|
||||
|
||||
def set_user_roles
|
||||
@user_roles = UserRole.where(highlighted: true).where.not(color: [nil, ''])
|
||||
end
|
||||
end
|
||||
|
|
10
app/controllers/redirect/accounts_controller.rb
Normal file
10
app/controllers/redirect/accounts_controller.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::AccountsController < Redirect::BaseController
|
||||
private
|
||||
|
||||
def set_resource
|
||||
@resource = Account.find(params[:id])
|
||||
not_found if @resource.local?
|
||||
end
|
||||
end
|
29
app/controllers/redirect/base_controller.rb
Normal file
29
app/controllers/redirect/base_controller.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::BaseController < ApplicationController
|
||||
vary_by 'Accept-Language'
|
||||
|
||||
before_action :set_pack
|
||||
before_action :set_resource
|
||||
before_action :set_app_body_class
|
||||
|
||||
def show
|
||||
@redirect_path = ActivityPub::TagManager.instance.url_for(@resource)
|
||||
|
||||
render 'redirects/show', layout: 'application'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_app_body_class
|
||||
@body_classes = 'app-body'
|
||||
end
|
||||
|
||||
def set_resource
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'public'
|
||||
end
|
||||
end
|
10
app/controllers/redirect/statuses_controller.rb
Normal file
10
app/controllers/redirect/statuses_controller.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Redirect::StatusesController < Redirect::BaseController
|
||||
private
|
||||
|
||||
def set_resource
|
||||
@resource = Status.find(params[:id])
|
||||
not_found if @resource.local? || !@resource.distributable?
|
||||
end
|
||||
end
|
|
@ -6,8 +6,8 @@ module Settings
|
|||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_otp_enabled
|
||||
before_action :require_webauthn_enabled, only: [:index, :destroy]
|
||||
before_action :redirect_invalid_otp, unless: -> { current_user.otp_enabled? }
|
||||
before_action :redirect_invalid_webauthn, only: [:index, :destroy], unless: -> { current_user.webauthn_enabled? }
|
||||
|
||||
def index; end
|
||||
def new; end
|
||||
|
@ -89,18 +89,14 @@ module Settings
|
|||
use_pack 'auth'
|
||||
end
|
||||
|
||||
def require_otp_enabled
|
||||
unless current_user.otp_enabled?
|
||||
flash[:error] = t('webauthn_credentials.otp_required')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
def redirect_invalid_otp
|
||||
flash[:error] = t('webauthn_credentials.otp_required')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
|
||||
def require_webauthn_enabled
|
||||
unless current_user.webauthn_enabled?
|
||||
flash[:error] = t('webauthn_credentials.not_enabled')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
def redirect_invalid_webauthn
|
||||
flash[:error] = t('webauthn_credentials.not_enabled')
|
||||
redirect_to settings_two_factor_authentication_methods_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,22 +31,26 @@ module AccountsHelper
|
|||
Setting.hide_followers_count || account.user&.settings&.[]('hide_followers_count')
|
||||
end
|
||||
|
||||
def account_formatted_stat(value)
|
||||
number_to_human(value, precision: 3, strip_insignificant_zeros: true)
|
||||
end
|
||||
|
||||
def account_description(account)
|
||||
prepend_stats = [
|
||||
[
|
||||
number_to_human(account.statuses_count, precision: 3, strip_insignificant_zeros: true),
|
||||
account_formatted_stat(account.statuses_count),
|
||||
I18n.t('accounts.posts', count: account.statuses_count),
|
||||
].join(' '),
|
||||
|
||||
[
|
||||
number_to_human(account.following_count, precision: 3, strip_insignificant_zeros: true),
|
||||
account_formatted_stat(account.following_count),
|
||||
I18n.t('accounts.following', count: account.following_count),
|
||||
].join(' '),
|
||||
]
|
||||
|
||||
unless hide_followers_count?(account)
|
||||
prepend_stats << [
|
||||
number_to_human(account.followers_count, precision: 3, strip_insignificant_zeros: true),
|
||||
account_formatted_stat(account.followers_count),
|
||||
I18n.t('accounts.followers', count: account.followers_count),
|
||||
].join(' ')
|
||||
end
|
||||
|
|
|
@ -155,8 +155,8 @@ module JsonLdHelper
|
|||
end
|
||||
end
|
||||
|
||||
def fetch_resource(uri, id, on_behalf_of = nil)
|
||||
unless id
|
||||
def fetch_resource(uri, id_is_known, on_behalf_of = nil, request_options: {})
|
||||
unless id_is_known
|
||||
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
||||
|
||||
return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id'])
|
||||
|
@ -164,14 +164,14 @@ module JsonLdHelper
|
|||
uri = json['id']
|
||||
end
|
||||
|
||||
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
||||
json = fetch_resource_without_id_validation(uri, on_behalf_of, request_options: request_options)
|
||||
json.present? && json['id'] == uri ? json : nil
|
||||
end
|
||||
|
||||
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false)
|
||||
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false, request_options: {})
|
||||
on_behalf_of ||= Account.representative
|
||||
|
||||
build_request(uri, on_behalf_of).perform do |response|
|
||||
build_request(uri, on_behalf_of, options: request_options).perform do |response|
|
||||
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error
|
||||
|
||||
body_to_json(response.body_with_limit) if response.code == 200
|
||||
|
@ -204,8 +204,8 @@ module JsonLdHelper
|
|||
response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code))
|
||||
end
|
||||
|
||||
def build_request(uri, on_behalf_of = nil)
|
||||
Request.new(:get, uri).tap do |request|
|
||||
def build_request(uri, on_behalf_of = nil, options: {})
|
||||
Request.new(:get, uri, **options).tap do |request|
|
||||
request.on_behalf_of(on_behalf_of) if on_behalf_of
|
||||
request.add_headers('Accept' => 'application/activity+json, application/ld+json')
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module MascotHelper
|
||||
def mascot_url
|
||||
full_asset_url(instance_presenter.mascot&.file&.url || asset_pack_path('media/images/elephant_ui_plane.svg'))
|
||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||
end
|
||||
|
||||
def instance_presenter
|
||||
|
|
|
@ -24,8 +24,12 @@ module RoutingHelper
|
|||
Rails.configuration.action_controller.asset_host || root_url
|
||||
end
|
||||
|
||||
def full_pack_url(source, **options)
|
||||
full_asset_url(asset_pack_path(source, **options))
|
||||
def frontend_asset_path(source, **options)
|
||||
asset_pack_path("media/#{source}", **options)
|
||||
end
|
||||
|
||||
def frontend_asset_url(source, **options)
|
||||
full_asset_url(frontend_asset_path(source, **options))
|
||||
end
|
||||
|
||||
def use_storage?
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default 'SvgrURL';
|
||||
export const ReactComponent = 'div';
|
||||
const ReactComponent = 'div';
|
||||
|
||||
export default ReactComponent;
|
||||
|
|
4
app/javascript/core/inert.js
Normal file
4
app/javascript/core/inert.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* Placeholder file to have `inert.scss` compiled by Webpack
|
||||
This is used by the `wicg-inert` polyfill */
|
||||
|
||||
import '../styles/inert.scss';
|
|
@ -10,6 +10,9 @@ pack:
|
|||
embed: embed.js
|
||||
error:
|
||||
home:
|
||||
inert:
|
||||
filename: inert.js
|
||||
stylesheet: true
|
||||
mailer:
|
||||
filename: mailer.js
|
||||
stylesheet: true
|
||||
|
|
|
@ -170,6 +170,11 @@ export const openURL = routerHistory => (dispatch, getState) => {
|
|||
|
||||
export const clickSearchResult = (q, type) => (dispatch, getState) => {
|
||||
const previous = getState().getIn(['search', 'recent']);
|
||||
|
||||
if (previous.some(x => x.get('q') === q && x.get('type') === type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const current = previous.add(fromJS({ type, q })).takeLast(4);
|
||||
|
||||
|
@ -198,4 +203,4 @@ export const hydrateSearch = () => (dispatch, getState) => {
|
|||
if (history !== null) {
|
||||
dispatch(updateSearchHistory(history));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,10 +7,10 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||
|
||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||
|
||||
export default class AttachmentList extends ImmutablePureComponent {
|
||||
|
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||
import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg';
|
||||
import GroupsIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
|
||||
|
||||
|
||||
export const Badge = ({ icon, label, domain }) => (
|
||||
|
|
|
@ -2,8 +2,7 @@ import { useCallback } from 'react';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context';
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
|||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg';
|
||||
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
|
||||
|
||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
||||
import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react';
|
||||
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import TuneIcon from '@/material-icons/400-24px/tune.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { ButtonInTabsBar, useColumnsContext } from 'flavours/glitch/features/ui/util/columns_context';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
||||
import { useAppHistory } from './router';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -8,8 +8,7 @@ import { useCallback, useState, useEffect } from 'react';
|
|||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import { changeSetting } from 'flavours/glitch/actions/settings';
|
||||
import { bannerSettings } from 'flavours/glitch/settings';
|
||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
|||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ import { withRouter } from 'react-router-dom';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import { CircularProgress } from 'flavours/glitch/components/circular_progress';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ArrowDropDownIcon } from '@material-symbols/svg-600/outlined/arrow_drop_down.svg';
|
||||
|
||||
import ArrowDropDownIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import InlineAccount from 'flavours/glitch/components/inline_account';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
|
||||
|
||||
import CheckBoxOutlineBlankIcon from '@/material-icons/400-24px/check_box_outline_blank.svg?react';
|
||||
import { isProduction } from 'flavours/glitch/utils/environment';
|
||||
|
||||
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
|
||||
|
|
|
@ -2,8 +2,7 @@ import { useCallback } from 'react';
|
|||
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import logo from 'mastodon/../images/logo.svg';
|
||||
import logo from '@/images/logo.svg';
|
||||
|
||||
export const WordmarkLogo = () => (
|
||||
<svg viewBox='0 0 261 66' className='logo logo--wordmark' role='img'>
|
||||
|
|
|
@ -8,9 +8,9 @@ import classNames from 'classnames';
|
|||
import { is } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||
|
||||
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
|
||||
|
@ -123,7 +123,7 @@ class Item extends PureComponent {
|
|||
}
|
||||
|
||||
if (attachment.get('description')?.length > 0) {
|
||||
badges.push(<span key='alt' className='media-gallery__gifv__label'>ALT</span>);
|
||||
badges.push(<span key='alt' className='media-gallery__alt__label'>ALT</span>);
|
||||
}
|
||||
|
||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||
|
|
|
@ -12,10 +12,10 @@ import classNames from 'classnames';
|
|||
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||
|
||||
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' },
|
||||
btnNone : { id: 'notification_purge.btn_none', defaultMessage: 'Select\nnone' },
|
||||
|
|
|
@ -5,8 +5,8 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CancelPresentationIcon } from '@material-symbols/svg-600/outlined/cancel_presentation.svg';
|
||||
|
||||
import CancelPresentationIcon from '@/material-icons/400-24px/cancel_presentation.svg?react';
|
||||
import { removePictureInPicture } from 'flavours/glitch/actions/picture_in_picture';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
||||
|
|
|
@ -8,22 +8,23 @@ import { withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AddReactionIcon } from '@material-symbols/svg-600/outlined/add_reaction.svg';
|
||||
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||
import { ReactComponent as BookmarkBorderIcon } from '@material-symbols/svg-600/outlined/bookmark.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
|
||||
import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg';
|
||||
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||
import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg';
|
||||
import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg';
|
||||
|
||||
import AddReactionIcon from '@/material-icons/400-24px/add_reaction.svg?react';
|
||||
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
|
||||
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
|
||||
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
||||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg';
|
||||
import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg';
|
||||
|
||||
import DropdownMenuContainer from '../containers/dropdown_menu_container';
|
||||
import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container';
|
||||
|
@ -315,7 +316,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
|
|
|
@ -9,16 +9,16 @@ import { withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
|
||||
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
|
||||
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||
|
||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
|
||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||
|
||||
|
||||
import { Permalink } from './permalink';
|
||||
|
||||
const textMatchesTarget = (text, origin, host) => {
|
||||
|
|
|
@ -6,18 +6,18 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as ExpandLessIcon } from '@material-symbols/svg-600/outlined/expand_less.svg';
|
||||
import { ReactComponent as ForumIcon } from '@material-symbols/svg-600/outlined/forum.svg';
|
||||
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home.svg';
|
||||
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
|
||||
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
|
||||
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||
|
||||
import ExpandLessIcon from '@/material-icons/400-24px/expand_less.svg?react';
|
||||
import ForumIcon from '@/material-icons/400-24px/forum.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
|
||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { languages } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { VisibilityIcon } from './visibility_icon';
|
||||
|
||||
|
|
|
@ -6,14 +6,13 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as AddReactionIcon } from '@material-symbols/svg-600/outlined/add_reaction.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
|
||||
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||
import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg';
|
||||
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||
|
||||
import AddReactionIcon from '@/material-icons/400-24px/add_reaction.svg?react';
|
||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
@ -94,7 +93,7 @@ export default class StatusPrepend extends PureComponent {
|
|||
<FormattedMessage
|
||||
id='notification.reaction'
|
||||
defaultMessage='{name} reacted to your status'
|
||||
values={{ name : linkifiedAccounts }}
|
||||
values={{ name: linkifiedAccounts }}
|
||||
/>
|
||||
);
|
||||
case 'reblog':
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ import { List as ImmutableList } from 'immutable';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||
import { ReactComponent as ExpandMoreIcon } from '@material-symbols/svg-600/outlined/expand_more.svg';
|
||||
|
||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||
import ExpandMoreIcon from '@/material-icons/400-24px/expand_more.svg?react';
|
||||
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server';
|
||||
import Column from 'flavours/glitch/components/column';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
|
|
@ -6,10 +6,10 @@ import { NavLink } from 'react-router-dom';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
|
||||
|
||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
||||
class ActionBar extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -30,7 +30,8 @@ class ActionBar extends PureComponent {
|
|||
return (
|
||||
<div>
|
||||
<div className='account__disclaimer'>
|
||||
<Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
|
||||
<Icon id='info-circle' icon={InfoIcon} />
|
||||
<FormattedMessage
|
||||
id='account.suspended_disclaimer_full'
|
||||
defaultMessage='This user has been suspended by a moderator.'
|
||||
/>
|
||||
|
@ -44,14 +45,17 @@ class ActionBar extends PureComponent {
|
|||
if (account.get('acct') !== account.get('username')) {
|
||||
extraInfo = (
|
||||
<div className='account__disclaimer'>
|
||||
<Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
|
||||
id='account.disclaimer_full'
|
||||
defaultMessage="Information below may reflect the user's profile incompletely."
|
||||
/>
|
||||
{' '}
|
||||
<a target='_blank' rel='noopener' href={account.get('url')}>
|
||||
<FormattedMessage id='account.view_full_profile' defaultMessage='View full profile' />
|
||||
</a>
|
||||
<Icon id='info-circle' icon={InfoIcon} />
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='account.disclaimer_full'
|
||||
defaultMessage="Information below may reflect the user's profile incompletely."
|
||||
/>
|
||||
{' '}
|
||||
<a target='_blank' rel='noopener' href={account.get('url')}>
|
||||
<FormattedMessage id='account.view_full_profile' defaultMessage='View full profile' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ import { FormattedMessage } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
|
|
@ -9,12 +9,12 @@ import { withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications.svg';
|
||||
import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active-fill.svg';
|
||||
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
||||
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
|
|
|
@ -3,7 +3,7 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AudiotrackIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg';
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
|
||||
import AudiotrackIcon from '@/material-icons/400-24px/music_note.svg?react';
|
||||
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow.svg?react';
|
||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
||||
export default class MediaItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
|
|
@ -5,8 +5,8 @@ import { withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as TripIcon } from '@material-symbols/svg-600/outlined/trip.svg';
|
||||
|
||||
import TripIcon from '@/material-icons/400-24px/trip.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
|
|
@ -7,17 +7,18 @@ import classNames from 'classnames';
|
|||
|
||||
import { is } from 'immutable';
|
||||
|
||||
import { ReactComponent as DownloadIcon } from '@material-symbols/svg-600/outlined/download.svg';
|
||||
import { ReactComponent as PauseIcon } from '@material-symbols/svg-600/outlined/pause.svg';
|
||||
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow-fill.svg';
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off-fill.svg';
|
||||
import { ReactComponent as VolumeUpIcon } from '@material-symbols/svg-600/outlined/volume_up-fill.svg';
|
||||
import { throttle, debounce } from 'lodash';
|
||||
|
||||
import DownloadIcon from '@/material-icons/400-24px/download.svg?react';
|
||||
import PauseIcon from '@/material-icons/400-24px/pause.svg?react';
|
||||
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react';
|
||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
||||
import VolumeOffIcon from '@/material-icons/400-24px/volume_off-fill.svg?react';
|
||||
import VolumeUpIcon from '@/material-icons/400-24px/volume_up-fill.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
|
||||
|
||||
|
||||
import { Blurhash } from '../../components/blurhash';
|
||||
import { displayMedia, useBlurhash } from '../../initial_state';
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
||||
|
||||
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
||||
import { LoadingIndicator } from '../../components/loading_indicator';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
|
|
|
@ -8,9 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
||||
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks';
|
||||
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
|
||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||
|
|
|
@ -7,8 +7,8 @@ import { Helmet } from 'react-helmet';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
|
||||
|
||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
||||
import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links';
|
||||
|
||||
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<div className='autosuggest-account' title={account.get('acct')}>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={24} /></div>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
|
||||
<DisplayName account={account} inline />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -70,7 +70,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
advancedOptions: ImmutablePropTypes.map,
|
||||
layout: PropTypes.string,
|
||||
media: ImmutablePropTypes.list,
|
||||
sideArm: PropTypes.string,
|
||||
sensitive: PropTypes.bool,
|
||||
|
@ -259,7 +258,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
intl,
|
||||
advancedOptions,
|
||||
isSubmitting,
|
||||
layout,
|
||||
onChangeSpoilerness,
|
||||
onPaste,
|
||||
privacy,
|
||||
|
@ -313,7 +311,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
||||
lang={this.props.lang}
|
||||
>
|
||||
<TextareaIcons advancedOptions={advancedOptions} />
|
||||
|
|
|
@ -7,18 +7,18 @@ import { Link } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
|
||||
import { ReactComponent as LogoutIcon } from '@material-symbols/svg-600/outlined/logout.svg';
|
||||
import { ReactComponent as ManufacturingIcon } from '@material-symbols/svg-600/outlined/manufacturing.svg';
|
||||
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
|
||||
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import LogoutIcon from '@/material-icons/400-24px/logout.svg?react';
|
||||
import ManufacturingIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
||||
import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { signOutLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { conditionalRender } from 'flavours/glitch/utils/react_helpers';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
community: {
|
||||
defaultMessage: 'Local timeline',
|
||||
|
|
|
@ -38,8 +38,8 @@ export default class NavigationBar extends ImmutablePureComponent {
|
|||
|
||||
{ profileLink !== undefined && (
|
||||
<a
|
||||
className='edit'
|
||||
href={profileLink}
|
||||
className='navigation-bar__profile-edit'
|
||||
><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -6,19 +6,20 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AttachFileIcon } from '@material-symbols/svg-600/outlined/attach_file.svg';
|
||||
import { ReactComponent as BrushIcon } from '@material-symbols/svg-600/outlined/brush.svg';
|
||||
import { ReactComponent as CodeIcon } from '@material-symbols/svg-600/outlined/code.svg';
|
||||
import { ReactComponent as DescriptionIcon } from '@material-symbols/svg-600/outlined/description.svg';
|
||||
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||
import { ReactComponent as MarkdownIcon } from '@material-symbols/svg-600/outlined/markdown.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as UploadFileIcon } from '@material-symbols/svg-600/outlined/upload_file.svg';
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import AttachFileIcon from '@/material-icons/400-24px/attach_file.svg?react';
|
||||
import BrushIcon from '@/material-icons/400-24px/brush.svg?react';
|
||||
import CodeIcon from '@/material-icons/400-24px/code.svg?react';
|
||||
import DescriptionIcon from '@/material-icons/400-24px/description.svg?react';
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import MarkdownIcon from '@/material-icons/400-24px/markdown.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import UploadFileIcon from '@/material-icons/400-24px/upload_file.svg?react';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import { pollLimits } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
||||
import DropdownContainer from '../containers/dropdown_container';
|
||||
import LanguageDropdown from '../containers/language_dropdown_container';
|
||||
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
||||
|
|
|
@ -8,9 +8,9 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import AutosuggestInput from 'flavours/glitch/components/autosuggest_input';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
|
|
|
@ -3,10 +3,10 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
|
||||
import Dropdown from './dropdown';
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
||||
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
publish: {
|
||||
defaultMessage: 'Publish',
|
||||
|
|
|
@ -5,8 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import { withRouter } from 'react-router-dom';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
|
||||
import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { domain, searchEnabled } from 'flavours/glitch/initial_state';
|
||||
import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags';
|
||||
|
@ -63,14 +63,14 @@ class Search extends PureComponent {
|
|||
};
|
||||
|
||||
defaultOptions = [
|
||||
{ label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:'); } },
|
||||
{ label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:'); } },
|
||||
{ label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:'); } },
|
||||
{ label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:'); } },
|
||||
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:'); } },
|
||||
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:'); } },
|
||||
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:'); } },
|
||||
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library', 'public']} /></>, action: e => { e.preventDefault(); this._insertText('in:'); } }
|
||||
{ key: 'prompt-has', label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:'); } },
|
||||
{ key: 'prompt-is', label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:'); } },
|
||||
{ key: 'prompt-language', label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:'); } },
|
||||
{ key: 'prompt-from', label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:'); } },
|
||||
{ key: 'prompt-before', label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:'); } },
|
||||
{ key: 'prompt-during', label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:'); } },
|
||||
{ key: 'prompt-after', label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:'); } },
|
||||
{ key: 'prompt-in', label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library', 'public']} /></>, action: e => { e.preventDefault(); this._insertText('in:'); } }
|
||||
];
|
||||
|
||||
setRef = c => {
|
||||
|
@ -263,6 +263,8 @@ class Search extends PureComponent {
|
|||
const { recent } = this.props;
|
||||
|
||||
return recent.toArray().map(search => ({
|
||||
key: `${search.get('type')}/${search.get('q')}`,
|
||||
|
||||
label: labelForRecentSearch(search),
|
||||
|
||||
action: () => this.handleRecentSearchClick(search),
|
||||
|
@ -347,8 +349,8 @@ class Search extends PureComponent {
|
|||
<h4><FormattedMessage id='search_popout.recent' defaultMessage='Recent searches' /></h4>
|
||||
|
||||
<div className='search__popout__menu'>
|
||||
{recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => (
|
||||
<button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
|
||||
{recent.size > 0 ? this._getOptions().map(({ label, key, action, forget }, i) => (
|
||||
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
|
||||
<span>{label}</span>
|
||||
<button className='icon-button' onMouseDown={forget}><Icon id='times' icon={CloseIcon} /></button>
|
||||
</button>
|
||||
|
|
|
@ -5,15 +5,15 @@ import { FormattedMessage } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg';
|
||||
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
|
||||
|
||||
import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { LoadMore } from 'flavours/glitch/components/load_more';
|
||||
import { SearchSection } from 'flavours/glitch/features/explore/components/search_section';
|
||||
|
||||
|
||||
import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
|
||||
import AccountContainer from '../../../containers/account_container';
|
||||
import StatusContainer from '../../../containers/status_container';
|
||||
|
@ -76,7 +76,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
|
||||
|
||||
return (
|
||||
<div className='drawer--results'>
|
||||
<div className='search-results'>
|
||||
<header className='search-results__header'>
|
||||
<Icon id='search' icon={SearchIcon} />
|
||||
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue