Compare commits
No commits in common. "686df3be2196d546b5d158e6ddc8bc22f89ff7bb" and "3b122c5662e72d014313a91bdd722de17862c074" have entirely different histories.
686df3be21
...
3b122c5662
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
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}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/sshd:1": {},
|
"ghcr.io/devcontainers/features/sshd:1": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
"runServices": ["app", "db", "redis"],
|
"runServices": ["app", "db", "redis"],
|
||||||
|
@ -15,16 +15,16 @@
|
||||||
"portsAttributes": {
|
"portsAttributes": {
|
||||||
"3000": {
|
"3000": {
|
||||||
"label": "web",
|
"label": "web",
|
||||||
"onAutoForward": "notify",
|
"onAutoForward": "notify"
|
||||||
},
|
},
|
||||||
"4000": {
|
"4000": {
|
||||||
"label": "stream",
|
"label": "stream",
|
||||||
"onAutoForward": "silent",
|
"onAutoForward": "silent"
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent",
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
|
|
||||||
"remoteEnv": {
|
"remoteEnv": {
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev",
|
"STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev",
|
||||||
"DISABLE_FORGERY_REQUEST_PROTECTION": "true",
|
"DISABLE_FORGERY_REQUEST_PROTECTION": "true",
|
||||||
"ES_ENABLED": "",
|
"ES_ENABLED": "",
|
||||||
"LIBRE_TRANSLATE_ENDPOINT": "",
|
"LIBRE_TRANSLATE_ENDPOINT": ""
|
||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"settings": {},
|
"settings": {},
|
||||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
|
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/sshd:1": {},
|
"ghcr.io/devcontainers/features/sshd:1": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
"forwardPorts": [3000, 4000],
|
"forwardPorts": [3000, 4000],
|
||||||
|
@ -14,17 +14,17 @@
|
||||||
"3000": {
|
"3000": {
|
||||||
"label": "web",
|
"label": "web",
|
||||||
"onAutoForward": "notify",
|
"onAutoForward": "notify",
|
||||||
"requireLocalPort": true,
|
"requireLocalPort": true
|
||||||
},
|
},
|
||||||
"4000": {
|
"4000": {
|
||||||
"label": "stream",
|
"label": "stream",
|
||||||
"onAutoForward": "silent",
|
"onAutoForward": "silent",
|
||||||
"requireLocalPort": true,
|
"requireLocalPort": true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent",
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"settings": {},
|
"settings": {},
|
||||||
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
|
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ services:
|
||||||
hard: -1
|
hard: -1
|
||||||
|
|
||||||
libretranslate:
|
libretranslate:
|
||||||
image: libretranslate/libretranslate:v1.5.4
|
image: libretranslate/libretranslate:v1.5.3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- lt-data:/home/libretranslate/.local
|
- lt-data:/home/libretranslate/.local
|
||||||
|
|
61
.github/workflows/build-security.yml
vendored
61
.github/workflows/build-security.yml
vendored
|
@ -1,61 +0,0 @@
|
||||||
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: true
|
|
||||||
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=schedule,pattern=${{ 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
|
|
||||||
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=schedule,pattern=${{ 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,8 +78,23 @@ jobs:
|
||||||
- name: Create database
|
- name: Create database
|
||||||
run: './bin/rails db:create'
|
run: './bin/rails db:create'
|
||||||
|
|
||||||
- name: Run historical migrations with data population
|
- name: Run migrations up to v2.0.0
|
||||||
run: './bin/rails tests:migrations:prepare_database'
|
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 all remaining migrations
|
- name: Run all remaining migrations
|
||||||
run: './bin/rails db:migrate'
|
run: './bin/rails db:migrate'
|
||||||
|
|
22
.github/workflows/test-migrations-two-step.yml
vendored
22
.github/workflows/test-migrations-two-step.yml
vendored
|
@ -45,7 +45,6 @@ jobs:
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
options: >-
|
options: >-
|
||||||
|
@ -78,11 +77,28 @@ jobs:
|
||||||
- name: Create database
|
- name: Create database
|
||||||
run: './bin/rails db:create'
|
run: './bin/rails db:create'
|
||||||
|
|
||||||
- name: Run historical migrations with data population
|
- name: Run migrations up to v2.0.0
|
||||||
run: './bin/rails tests:migrations:prepare_database'
|
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'
|
||||||
env:
|
env:
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
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
|
- name: Run all remaining pre-deployment migrations
|
||||||
run: './bin/rails db:migrate'
|
run: './bin/rails db:migrate'
|
||||||
env:
|
env:
|
||||||
|
|
16
.github/workflows/test-ruby.yml
vendored
16
.github/workflows/test-ruby.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
|
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v3
|
||||||
if: matrix.mode == 'test'
|
if: matrix.mode == 'test'
|
||||||
with:
|
with:
|
||||||
path: |-
|
path: |-
|
||||||
|
@ -117,7 +117,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: './'
|
path: './'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
@ -193,7 +193,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: './public'
|
path: './public'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
@ -213,14 +213,14 @@ jobs:
|
||||||
- run: bundle exec rake spec:system
|
- run: bundle exec rake spec:system
|
||||||
|
|
||||||
- name: Archive logs
|
- name: Archive logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-logs-${{ matrix.ruby-version }}
|
name: e2e-logs-${{ matrix.ruby-version }}
|
||||||
path: log/
|
path: log/
|
||||||
|
|
||||||
- name: Archive test screenshots
|
- name: Archive test screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: e2e-screenshots
|
name: e2e-screenshots
|
||||||
|
@ -297,7 +297,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: './public'
|
path: './public'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
@ -317,14 +317,14 @@ jobs:
|
||||||
- run: bin/rspec --tag search
|
- run: bin/rspec --tag search
|
||||||
|
|
||||||
- name: Archive logs
|
- name: Archive logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: test-search-logs-${{ matrix.ruby-version }}
|
name: test-search-logs-${{ matrix.ruby-version }}
|
||||||
path: log/
|
path: log/
|
||||||
|
|
||||||
- name: Archive test screenshots
|
- name: Archive test screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: test-search-screenshots
|
name: test-search-screenshots
|
||||||
|
|
21
.rubocop.yml
21
.rubocop.yml
|
@ -103,26 +103,9 @@ Rails/Exit:
|
||||||
- 'config/boot.rb'
|
- 'config/boot.rb'
|
||||||
- 'lib/mastodon/cli/*.rb'
|
- 'lib/mastodon/cli/*.rb'
|
||||||
|
|
||||||
# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
|
|
||||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
|
|
||||||
Rails/LexicallyScopedActionFilter:
|
|
||||||
Exclude:
|
|
||||||
- '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:
|
Rails/SkipsModelValidations:
|
||||||
Enabled: false
|
Exclude:
|
||||||
|
- 'db/*migrate/**/*'
|
||||||
|
|
||||||
# Reason: We want to preserve the ability to migrate from arbitrary old versions,
|
# 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.
|
# and cannot guarantee that every installation has run every migration as they upgrade.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||||
# using RuboCop version 1.60.2.
|
# using RuboCop version 1.59.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
|
@ -13,6 +13,13 @@ Bundler/OrderedGems:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'Gemfile'
|
- '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:
|
Lint/NonLocalExitFromIterator:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/helpers/jsonld_helper.rb'
|
- 'app/helpers/jsonld_helper.rb'
|
||||||
|
@ -57,10 +64,64 @@ Rails/HasAndBelongsToMany:
|
||||||
- 'app/models/status.rb'
|
- 'app/models/status.rb'
|
||||||
- 'app/models/tag.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:
|
Rails/OutputSafety:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/initializers/simple_form.rb'
|
- '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.
|
# Configuration parameters: Include.
|
||||||
# Include: app/models/**/*.rb
|
# Include: app/models/**/*.rb
|
||||||
Rails/UniqueValidationWithoutIndex:
|
Rails/UniqueValidationWithoutIndex:
|
||||||
|
@ -70,6 +131,38 @@ Rails/UniqueValidationWithoutIndex:
|
||||||
- 'app/models/identity.rb'
|
- 'app/models/identity.rb'
|
||||||
- 'app/models/webauthn_credential.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).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
# AllowedMethods: ==, equal?, eql?
|
# AllowedMethods: ==, equal?, eql?
|
||||||
|
@ -108,6 +201,7 @@ Style/FetchEnvVar:
|
||||||
# AllowedMethods: redirect
|
# AllowedMethods: redirect
|
||||||
Style/FormatStringToken:
|
Style/FormatStringToken:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
- 'app/models/privacy_policy.rb'
|
||||||
- 'config/initializers/devise.rb'
|
- 'config/initializers/devise.rb'
|
||||||
- 'lib/paperclip/color_extractor.rb'
|
- 'lib/paperclip/color_extractor.rb'
|
||||||
|
|
||||||
|
@ -121,6 +215,10 @@ Style/GlobalStdStream:
|
||||||
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
||||||
Style/GuardClause:
|
Style/GuardClause:
|
||||||
Exclude:
|
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/activitypub/activity/block.rb'
|
||||||
- 'app/lib/request.rb'
|
- 'app/lib/request.rb'
|
||||||
- 'app/lib/request_pool.rb'
|
- 'app/lib/request_pool.rb'
|
||||||
|
@ -275,6 +373,13 @@ Style/StringLiterals:
|
||||||
- 'config/initializers/webauthn.rb'
|
- 'config/initializers/webauthn.rb'
|
||||||
- 'config/routes.rb'
|
- 'config/routes.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).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyleForMultiline.
|
# Configuration parameters: EnforcedStyleForMultiline.
|
||||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.2.3
|
3.2.2
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
||||||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||||
|
|
||||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.3"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.2"]
|
||||||
ARG RUBY_VERSION="3.2.3"
|
ARG RUBY_VERSION="3.2.2"
|
||||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||||
ARG NODE_MAJOR_VERSION="20"
|
ARG NODE_MAJOR_VERSION="20"
|
||||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||||
ARG DEBIAN_VERSION="bookworm"
|
ARG DEBIAN_VERSION="bookworm"
|
||||||
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||||
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
|
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
|
||||||
# Ruby image to use for base image based on combined variables (ex: 3.2.3-slim-bookworm)
|
# Ruby image to use for base image based on combined variables (ex: 3.2.2-slim-bookworm)
|
||||||
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
|
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
|
||||||
|
|
||||||
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
||||||
|
|
|
@ -1,35 +1,19 @@
|
||||||
# Federation
|
## ActivityPub federation in Mastodon
|
||||||
|
|
||||||
## 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.
|
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 ActivityPub vocabulary](https://docs.joinmastodon.org/spec/activitypub/)
|
Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/
|
||||||
|
|
||||||
### Required extensions
|
### Required extensions
|
||||||
|
|
||||||
#### WebFinger
|
#### Webfinger
|
||||||
|
|
||||||
In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`).
|
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.
|
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.
|
As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger.
|
||||||
|
|
||||||
- [WebFinger information and examples](https://docs.joinmastodon.org/spec/webfinger/)
|
More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/
|
||||||
|
|
||||||
#### HTTP Signatures
|
#### HTTP Signatures
|
||||||
|
|
||||||
|
@ -37,13 +21,11 @@ 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.
|
Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server.
|
||||||
|
|
||||||
- [HTTP Signatures information and examples](https://docs.joinmastodon.org/spec/security/#http)
|
More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http
|
||||||
|
|
||||||
### Optional extensions
|
### Optional extensions
|
||||||
|
|
||||||
- [Linked-Data Signatures](https://docs.joinmastodon.org/spec/security/#ld)
|
- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
|
||||||
- [Bearcaps](https://docs.joinmastodon.org/spec/bearcaps/)
|
- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
|
||||||
|
- Followers collection synchronization: https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
|
||||||
### Additional documentation
|
- Search indexing consent for actors: https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md
|
||||||
|
|
||||||
- [Mastodon documentation](https://docs.joinmastodon.org/)
|
|
||||||
|
|
5
Gemfile
5
Gemfile
|
@ -39,7 +39,8 @@ end
|
||||||
|
|
||||||
gem 'net-ldap', '~> 0.18'
|
gem 'net-ldap', '~> 0.18'
|
||||||
|
|
||||||
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
# TODO: Point back at released omniauth-cas gem when new version is released
|
||||||
|
gem 'omniauth-cas', github: 'dlindahl/omniauth-cas', ref: '9d9d3a91b316c55d49ab6e621977f2067010c5bf'
|
||||||
gem 'omniauth-saml', '~> 2.0'
|
gem 'omniauth-saml', '~> 2.0'
|
||||||
gem 'omniauth_openid_connect', '~> 0.6.1'
|
gem 'omniauth_openid_connect', '~> 0.6.1'
|
||||||
gem 'omniauth', '~> 2.0'
|
gem 'omniauth', '~> 2.0'
|
||||||
|
@ -123,7 +124,7 @@ group :test do
|
||||||
gem 'database_cleaner-active_record'
|
gem 'database_cleaner-active_record'
|
||||||
|
|
||||||
# Used to mock environment variables
|
# Used to mock environment variables
|
||||||
gem 'climate_control'
|
gem 'climate_control', '~> 0.2'
|
||||||
|
|
||||||
# Generating fake data for specs
|
# Generating fake data for specs
|
||||||
gem 'faker', '~> 3.2'
|
gem 'faker', '~> 3.2'
|
||||||
|
|
177
Gemfile.lock
177
Gemfile.lock
|
@ -7,6 +7,16 @@ GIT
|
||||||
hkdf (~> 0.2)
|
hkdf (~> 0.2)
|
||||||
jwt (~> 2.0)
|
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
|
GIT
|
||||||
remote: https://github.com/jhawthorn/nsa.git
|
remote: https://github.com/jhawthorn/nsa.git
|
||||||
revision: e020fcc3a54d993ab45b7194d89ab720296c111b
|
revision: e020fcc3a54d993ab45b7194d89ab720296c111b
|
||||||
|
@ -21,35 +31,35 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.1.3)
|
actioncable (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (7.1.3)
|
actionmailbox (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
activejob (= 7.1.3)
|
activejob (= 7.1.2)
|
||||||
activerecord (= 7.1.3)
|
activerecord (= 7.1.2)
|
||||||
activestorage (= 7.1.3)
|
activestorage (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.1.3)
|
actionmailer (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
actionview (= 7.1.3)
|
actionview (= 7.1.2)
|
||||||
activejob (= 7.1.3)
|
activejob (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (7.1.3)
|
actionpack (7.1.2)
|
||||||
actionview (= 7.1.3)
|
actionview (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
racc
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
|
@ -57,15 +67,15 @@ GEM
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
actiontext (7.1.3)
|
actiontext (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
activerecord (= 7.1.3)
|
activerecord (= 7.1.2)
|
||||||
activestorage (= 7.1.3)
|
activestorage (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.1.3)
|
actionview (7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
|
@ -75,22 +85,22 @@ GEM
|
||||||
activemodel (>= 4.1)
|
activemodel (>= 4.1)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (7.1.3)
|
activejob (7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.1.3)
|
activemodel (7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
activerecord (7.1.3)
|
activerecord (7.1.2)
|
||||||
activemodel (= 7.1.3)
|
activemodel (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (7.1.3)
|
activestorage (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
activejob (= 7.1.3)
|
activejob (= 7.1.2)
|
||||||
activerecord (= 7.1.3)
|
activerecord (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (7.1.3)
|
activesupport (7.1.2)
|
||||||
base64
|
base64
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
@ -150,12 +160,12 @@ GEM
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
parser (>= 2.4)
|
parser (>= 2.4)
|
||||||
smart_properties
|
smart_properties
|
||||||
bigdecimal (3.1.6)
|
bigdecimal (3.1.5)
|
||||||
bindata (2.4.15)
|
bindata (2.4.15)
|
||||||
binding_of_caller (1.0.0)
|
binding_of_caller (1.0.0)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
blurhash (0.1.7)
|
blurhash (0.1.7)
|
||||||
bootsnap (1.17.1)
|
bootsnap (1.17.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (6.1.1)
|
brakeman (6.1.1)
|
||||||
racc
|
racc
|
||||||
|
@ -180,15 +190,15 @@ GEM
|
||||||
activesupport
|
activesupport
|
||||||
cbor (0.5.9.6)
|
cbor (0.5.9.6)
|
||||||
charlock_holmes (0.7.7)
|
charlock_holmes (0.7.7)
|
||||||
chewy (7.5.0)
|
chewy (7.4.0)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||||
elasticsearch-dsl
|
elasticsearch-dsl
|
||||||
chunky_png (1.4.0)
|
chunky_png (1.4.0)
|
||||||
climate_control (1.2.0)
|
climate_control (0.2.0)
|
||||||
cocoon (1.2.15)
|
cocoon (1.2.15)
|
||||||
color_diff (0.1)
|
color_diff (0.1)
|
||||||
concurrent-ruby (1.2.3)
|
concurrent-ruby (1.2.2)
|
||||||
connection_pool (2.4.1)
|
connection_pool (2.4.1)
|
||||||
cose (1.3.0)
|
cose (1.3.0)
|
||||||
cbor (~> 0.5.9)
|
cbor (~> 0.5.9)
|
||||||
|
@ -257,7 +267,7 @@ GEM
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (0.109.0)
|
excon (0.109.0)
|
||||||
fabrication (2.31.0)
|
fabrication (2.31.0)
|
||||||
faker (3.2.3)
|
faker (3.2.2)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (1.10.3)
|
faraday (1.10.3)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
|
@ -319,7 +329,7 @@ GEM
|
||||||
activesupport (>= 5.1)
|
activesupport (>= 5.1)
|
||||||
haml (>= 4.0.6)
|
haml (>= 4.0.6)
|
||||||
railties (>= 5.1)
|
railties (>= 5.1)
|
||||||
haml_lint (0.55.0)
|
haml_lint (0.53.0)
|
||||||
haml (>= 5.0)
|
haml (>= 5.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
rainbow
|
rainbow
|
||||||
|
@ -360,7 +370,7 @@ GEM
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
terminal-table (>= 1.5.1)
|
terminal-table (>= 1.5.1)
|
||||||
idn-ruby (0.1.5)
|
idn-ruby (0.1.5)
|
||||||
io-console (0.7.2)
|
io-console (0.7.1)
|
||||||
irb (1.11.1)
|
irb (1.11.1)
|
||||||
rdoc
|
rdoc
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
|
@ -398,12 +408,12 @@ GEM
|
||||||
activerecord
|
activerecord
|
||||||
kaminari-core (= 1.2.2)
|
kaminari-core (= 1.2.2)
|
||||||
kaminari-core (1.2.2)
|
kaminari-core (1.2.2)
|
||||||
kt-paperclip (7.2.2)
|
kt-paperclip (7.2.1)
|
||||||
activemodel (>= 4.2.0)
|
activemodel (>= 4.2.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
marcel (~> 1.0.1)
|
marcel (~> 1.0.1)
|
||||||
mime-types
|
mime-types
|
||||||
terrapin (>= 0.6.0, < 2.0)
|
terrapin (~> 0.6.0)
|
||||||
language_server-protocol (3.17.0.3)
|
language_server-protocol (3.17.0.3)
|
||||||
launchy (2.5.2)
|
launchy (2.5.2)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
|
@ -445,7 +455,7 @@ GEM
|
||||||
mime-types-data (3.2023.1205)
|
mime-types-data (3.2023.1205)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.5)
|
mini_portile2 (2.8.5)
|
||||||
minitest (5.21.2)
|
minitest (5.20.0)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.2)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.3.0)
|
multipart-post (2.3.0)
|
||||||
|
@ -454,7 +464,7 @@ GEM
|
||||||
uri
|
uri
|
||||||
net-http-persistent (4.0.2)
|
net-http-persistent (4.0.2)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.4.9.1)
|
net-imap (0.4.4)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
|
@ -462,7 +472,7 @@ GEM
|
||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.2)
|
net-protocol (0.2.2)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.4.0.1)
|
net-smtp (0.4.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.5.9)
|
nio4r (2.5.9)
|
||||||
nokogiri (1.16.0)
|
nokogiri (1.16.0)
|
||||||
|
@ -474,10 +484,6 @@ GEM
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
rack (>= 2.2.3)
|
rack (>= 2.2.3)
|
||||||
rack-protection
|
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)
|
omniauth-rails_csrf_protection (1.0.1)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 4.2)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
|
@ -504,7 +510,7 @@ GEM
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ox (2.14.17)
|
ox (2.14.17)
|
||||||
parallel (1.24.0)
|
parallel (1.24.0)
|
||||||
parser (3.3.0.5)
|
parser (3.2.2.4)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
|
@ -552,27 +558,27 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-proxy (0.7.6)
|
rack-proxy (0.7.6)
|
||||||
rack
|
rack
|
||||||
rack-session (1.0.2)
|
rack-session (1.0.1)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rackup (1.0.0)
|
rackup (1.0.0)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (7.1.3)
|
rails (7.1.2)
|
||||||
actioncable (= 7.1.3)
|
actioncable (= 7.1.2)
|
||||||
actionmailbox (= 7.1.3)
|
actionmailbox (= 7.1.2)
|
||||||
actionmailer (= 7.1.3)
|
actionmailer (= 7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
actiontext (= 7.1.3)
|
actiontext (= 7.1.2)
|
||||||
actionview (= 7.1.3)
|
actionview (= 7.1.2)
|
||||||
activejob (= 7.1.3)
|
activejob (= 7.1.2)
|
||||||
activemodel (= 7.1.3)
|
activemodel (= 7.1.2)
|
||||||
activerecord (= 7.1.3)
|
activerecord (= 7.1.2)
|
||||||
activestorage (= 7.1.3)
|
activestorage (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.1.3)
|
railties (= 7.1.2)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
actionview (>= 5.0.1.rc1)
|
actionview (>= 5.0.1.rc1)
|
||||||
|
@ -587,9 +593,9 @@ GEM
|
||||||
rails-i18n (7.0.8)
|
rails-i18n (7.0.8)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (7.1.3)
|
railties (7.1.2)
|
||||||
actionpack (= 7.1.3)
|
actionpack (= 7.1.2)
|
||||||
activesupport (= 7.1.3)
|
activesupport (= 7.1.2)
|
||||||
irb
|
irb
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
|
@ -600,8 +606,8 @@ GEM
|
||||||
rdf (3.3.1)
|
rdf (3.3.1)
|
||||||
bcp47_spec (~> 0.2)
|
bcp47_spec (~> 0.2)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.7.0)
|
rdf-normalize (0.6.1)
|
||||||
rdf (~> 3.3)
|
rdf (~> 3.2)
|
||||||
rdoc (6.6.2)
|
rdoc (6.6.2)
|
||||||
psych (>= 4.0.0)
|
psych (>= 4.0.0)
|
||||||
redcarpet (3.6.0)
|
redcarpet (3.6.0)
|
||||||
|
@ -610,7 +616,7 @@ GEM
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.9.0)
|
regexp_parser (2.8.3)
|
||||||
reline (0.4.2)
|
reline (0.4.2)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
request_store (1.5.1)
|
request_store (1.5.1)
|
||||||
|
@ -636,7 +642,7 @@ GEM
|
||||||
rspec-mocks (3.12.6)
|
rspec-mocks (3.12.6)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-rails (6.1.1)
|
rspec-rails (6.1.0)
|
||||||
actionpack (>= 6.1)
|
actionpack (>= 6.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
railties (>= 6.1)
|
railties (>= 6.1)
|
||||||
|
@ -650,11 +656,11 @@ GEM
|
||||||
rspec-mocks (~> 3.0)
|
rspec-mocks (~> 3.0)
|
||||||
sidekiq (>= 5, < 8)
|
sidekiq (>= 5, < 8)
|
||||||
rspec-support (3.12.1)
|
rspec-support (3.12.1)
|
||||||
rubocop (1.60.2)
|
rubocop (1.59.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.3.0.2)
|
parser (>= 3.2.2.4)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml (>= 3.2.5, < 4.0)
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
@ -696,8 +702,7 @@ GEM
|
||||||
scenic (1.7.0)
|
scenic (1.7.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
selenium-webdriver (4.17.0)
|
selenium-webdriver (4.16.0)
|
||||||
base64 (~> 0.2)
|
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
rubyzip (>= 1.2.2, < 3.0)
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
websocket (~> 1.0)
|
websocket (~> 1.0)
|
||||||
|
@ -731,7 +736,7 @@ GEM
|
||||||
simplecov-lcov (0.8.0)
|
simplecov-lcov (0.8.0)
|
||||||
simplecov_json_formatter (0.1.4)
|
simplecov_json_formatter (0.1.4)
|
||||||
smart_properties (1.17.0)
|
smart_properties (1.17.0)
|
||||||
stackprof (0.2.26)
|
stackprof (0.2.25)
|
||||||
statsd-ruby (1.5.0)
|
statsd-ruby (1.5.0)
|
||||||
stoplight (3.0.2)
|
stoplight (3.0.2)
|
||||||
redlock (~> 1.0)
|
redlock (~> 1.0)
|
||||||
|
@ -746,8 +751,8 @@ GEM
|
||||||
temple (0.10.3)
|
temple (0.10.3)
|
||||||
terminal-table (3.0.2)
|
terminal-table (3.0.2)
|
||||||
unicode-display_width (>= 1.1.1, < 3)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
terrapin (1.0.1)
|
terrapin (0.6.0)
|
||||||
climate_control
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
test-prof (1.3.1)
|
test-prof (1.3.1)
|
||||||
thor (1.3.0)
|
thor (1.3.0)
|
||||||
tilt (2.3.0)
|
tilt (2.3.0)
|
||||||
|
@ -836,7 +841,7 @@ DEPENDENCIES
|
||||||
capybara (~> 3.39)
|
capybara (~> 3.39)
|
||||||
charlock_holmes (~> 0.7.7)
|
charlock_holmes (~> 0.7.7)
|
||||||
chewy (~> 7.3)
|
chewy (~> 7.3)
|
||||||
climate_control
|
climate_control (~> 0.2)
|
||||||
cocoon (~> 1.2)
|
cocoon (~> 1.2)
|
||||||
color_diff (~> 0.1)
|
color_diff (~> 0.1)
|
||||||
concurrent-ruby
|
concurrent-ruby
|
||||||
|
@ -889,7 +894,7 @@ DEPENDENCIES
|
||||||
nsa!
|
nsa!
|
||||||
oj (~> 3.14)
|
oj (~> 3.14)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
omniauth-cas (~> 3.0.0.beta.1)
|
omniauth-cas!
|
||||||
omniauth-rails_csrf_protection (~> 1.0)
|
omniauth-rails_csrf_protection (~> 1.0)
|
||||||
omniauth-saml (~> 2.0)
|
omniauth-saml (~> 2.0)
|
||||||
omniauth_openid_connect (~> 0.6.1)
|
omniauth_openid_connect (~> 0.6.1)
|
||||||
|
@ -956,4 +961,4 @@ RUBY VERSION
|
||||||
ruby 3.2.2p53
|
ruby 3.2.2p53
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.4
|
2.4.20
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_items
|
def set_items
|
||||||
@items = @account.followers.matches_uri_prefix(uri_prefix).pluck(:uri)
|
@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)
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection_presenter
|
def collection_presenter
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||||
|
|
||||||
def unknown_affected_account?
|
def unknown_affected_account?
|
||||||
json = Oj.load(body, mode: :strict)
|
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.exists?(uri: json['actor'])
|
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?
|
||||||
rescue Oj::ParseError
|
rescue Oj::ParseError
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Admin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :audit_log, :index?
|
authorize :audit_log, :index?
|
||||||
@auditable_accounts = Account.auditable.select(:id, :username)
|
@auditable_accounts = Account.where(id: Admin::ActionLog.select('distinct account_id')).select(:id, :username)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
module Admin
|
module Admin
|
||||||
class ConfirmationsController < BaseController
|
class ConfirmationsController < BaseController
|
||||||
before_action :set_user
|
before_action :set_user
|
||||||
before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed?
|
before_action :check_confirmation, only: [:resend]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @user, :confirm?
|
authorize @user, :confirm?
|
||||||
@user.mark_email_as_confirmed!
|
@user.confirm!
|
||||||
log_action :confirm, @user
|
log_action :confirm, @user
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
|
@ -25,13 +25,11 @@ module Admin
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def redirect_confirmed_user
|
def check_confirmation
|
||||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
if @user.confirmed?
|
||||||
redirect_to admin_accounts_path
|
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||||
end
|
redirect_to admin_accounts_path
|
||||||
|
end
|
||||||
def user_confirmed?
|
|
||||||
@user.confirmed?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,7 @@ module Admin
|
||||||
log_action :create, @email_domain_block
|
log_action :create, @email_domain_block
|
||||||
|
|
||||||
(@email_domain_block.other_domains || []).uniq.each do |domain|
|
(@email_domain_block.other_domains || []).uniq.each do |domain|
|
||||||
next if EmailDomainBlock.exists?(domain: domain)
|
next if EmailDomainBlock.where(domain: domain).exists?
|
||||||
|
|
||||||
other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block)
|
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
|
log_action :create, other_email_domain_block
|
||||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
@warning_domains = instances_from_imported_blocks.pluck(:domain)
|
@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)
|
||||||
rescue ActionController::ParameterMissing
|
rescue ActionController::ParameterMissing
|
||||||
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
|
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
|
||||||
set_dummy_import!
|
set_dummy_import!
|
||||||
|
@ -58,10 +58,6 @@ module Admin
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def instances_from_imported_blocks
|
|
||||||
Instance.with_domain_follows(@domain_blocks.map(&:domain))
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_filename
|
def export_filename
|
||||||
'domain_blocks.csv'
|
'domain_blocks.csv'
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||||
return [] if hide_results?
|
return [] if hide_results?
|
||||||
|
|
||||||
scope = default_accounts
|
scope = default_accounts
|
||||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil? || current_account.id == @account.id
|
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
|
||||||
scope.merge(paginated_follows).to_a
|
scope.merge(paginated_follows).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:active_relationships, :account_stat, :user).references(:active_relationships)
|
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follows
|
def paginated_follows
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||||
return [] if hide_results?
|
return [] if hide_results?
|
||||||
|
|
||||||
scope = default_accounts
|
scope = default_accounts
|
||||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil? || current_account.id == @account.id
|
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
|
||||||
scope.merge(paginated_follows).to_a
|
scope.merge(paginated_follows).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:passive_relationships, :account_stat, :user).references(:passive_relationships)
|
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follows
|
def paginated_follows
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
# 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
|
end
|
||||||
|
|
||||||
def paginated_blocks
|
def paginated_blocks
|
||||||
@paginated_blocks ||= Block.eager_load(target_account: [:account_stat, :user])
|
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
||||||
.joins(:target_account)
|
.joins(:target_account)
|
||||||
.merge(Account.without_suspended)
|
.merge(Account.without_suspended)
|
||||||
.where(account: current_account)
|
.where(account: current_account)
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Api::V1::DirectoriesController < Api::BaseController
|
||||||
scope.merge!(local_account_scope) if local_accounts?
|
scope.merge!(local_account_scope) if local_accounts?
|
||||||
scope.merge!(account_exclusion_scope) if current_account
|
scope.merge!(account_exclusion_scope) if current_account
|
||||||
scope.merge!(account_domain_block_scope) if current_account && !local_accounts?
|
scope.merge!(account_domain_block_scope) if current_account && !local_accounts?
|
||||||
end.includes(:account_stat, user: :role)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def local_accounts?
|
def local_accounts?
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def endorsed_accounts
|
def endorsed_accounts
|
||||||
current_account.endorsed_accounts.includes(:account_stat, :user).without_suspended
|
current_account.endorsed_accounts.includes(:account_stat).without_suspended
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.without_suspended.includes(:follow_requests, :account_stat, :user).references(:follow_requests)
|
Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follow_requests
|
def paginated_follow_requests
|
||||||
|
|
|
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
if unlimited?
|
if unlimited?
|
||||||
@list.accounts.without_suspended.includes(:account_stat, :user).all
|
@list.accounts.without_suspended.includes(:account_stat).all
|
||||||
else
|
else
|
||||||
@list.accounts.without_suspended.includes(:account_stat, :user).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
@list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Api::V1::MarkersController < Api::BaseController
|
||||||
@markers = {}
|
@markers = {}
|
||||||
|
|
||||||
resource_params.each_pair do |timeline, timeline_params|
|
resource_params.each_pair do |timeline, timeline_params|
|
||||||
@markers[timeline] = current_user.markers.find_or_create_by(timeline: timeline)
|
@markers[timeline] = current_user.markers.find_or_initialize_by(timeline: timeline)
|
||||||
@markers[timeline].update!(timeline_params)
|
@markers[timeline].update!(timeline_params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Api::V1::MutesController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_mutes
|
def paginated_mutes
|
||||||
@paginated_mutes ||= Mute.eager_load(target_account: [:account_stat, :user])
|
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||||
.joins(:target_account)
|
.joins(:target_account)
|
||||||
.merge(Account.without_suspended)
|
.merge(Account.without_suspended)
|
||||||
.where(account: current_account)
|
.where(account: current_account)
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Api::V1::Peers::SearchController < Api::BaseController
|
||||||
@domains = InstancesIndex.query(function_score: {
|
@domains = InstancesIndex.query(function_score: {
|
||||||
query: {
|
query: {
|
||||||
prefix: {
|
prefix: {
|
||||||
domain: normalized_domain,
|
domain: TagManager.instance.normalize_domain(params[:q].strip),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -37,18 +37,11 @@ class Api::V1::Peers::SearchController < Api::BaseController
|
||||||
},
|
},
|
||||||
}).limit(10).pluck(:domain)
|
}).limit(10).pluck(:domain)
|
||||||
else
|
else
|
||||||
domain = normalized_domain
|
domain = params[:q].strip
|
||||||
@domains = Instance.searchable.domain_starts_with(domain).limit(10).pluck(:domain)
|
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)
|
||||||
end
|
end
|
||||||
rescue Addressable::URI::InvalidURIError
|
rescue Addressable::URI::InvalidURIError
|
||||||
@domains = []
|
@domains = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalized_domain
|
|
||||||
TagManager.instance.normalize_domain(query_value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def query_value
|
|
||||||
params[:q].strip
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,14 +14,14 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::Bas
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
scope = default_accounts
|
scope = default_accounts
|
||||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||||
scope.merge(paginated_favourites).to_a
|
scope.merge(paginated_favourites).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account
|
Account
|
||||||
.without_suspended
|
.without_suspended
|
||||||
.includes(:favourites, :account_stat, :user)
|
.includes(:favourites, :account_stat)
|
||||||
.references(:favourites)
|
.references(:favourites)
|
||||||
.where(favourites: { status_id: @status.id })
|
.where(favourites: { status_id: @status.id })
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,12 +14,12 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::Base
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
scope = default_accounts
|
scope = default_accounts
|
||||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||||
scope.merge(paginated_statuses).to_a
|
scope.merge(paginated_statuses).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.without_suspended.includes(:statuses, :account_stat, :user).references(:statuses)
|
Account.without_suspended.includes(:statuses, :account_stat).references(:statuses)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_statuses
|
def paginated_statuses
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Api::V2::FiltersController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_filters
|
def set_filters
|
||||||
@filters = current_account.custom_filters.includes(:keywords, :statuses)
|
@filters = current_account.custom_filters.includes(:keywords)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_filter
|
def set_filter
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
before_action :set_pack
|
before_action :set_pack
|
||||||
before_action :set_confirmation_user!, only: [:show, :confirm_captcha]
|
before_action :set_confirmation_user!, only: [:show, :confirm_captcha]
|
||||||
before_action :redirect_confirmed_user, if: :signed_in_confirmed_user?
|
before_action :require_unconfirmed!
|
||||||
|
|
||||||
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
||||||
before_action :require_captcha_if_needed!, only: [:show]
|
before_action :require_captcha_if_needed!, only: [:show]
|
||||||
|
@ -70,12 +70,10 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||||
use_pack 'auth'
|
use_pack 'auth'
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_confirmed_user
|
def require_unconfirmed!
|
||||||
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
|
if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def set_body_classes
|
def set_body_classes
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class Auth::PasswordsController < Devise::PasswordsController
|
class Auth::PasswordsController < Devise::PasswordsController
|
||||||
skip_before_action :check_self_destruct!
|
skip_before_action :check_self_destruct!
|
||||||
before_action :redirect_invalid_reset_token, only: :edit, unless: :reset_password_token_is_valid?
|
before_action :check_validity_of_reset_password_token, only: :edit
|
||||||
before_action :set_pack
|
before_action :set_pack
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
|
|
||||||
|
@ -20,9 +20,11 @@ class Auth::PasswordsController < Devise::PasswordsController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def redirect_invalid_reset_token
|
def check_validity_of_reset_password_token
|
||||||
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
unless reset_password_token_is_valid?
|
||||||
redirect_to new_password_path(resource_name)
|
flash[:error] = I18n.t('auth.invalid_reset_password_token')
|
||||||
|
redirect_to new_password_path(resource_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_body_classes
|
def set_body_classes
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Auth::SessionsController < Devise::SessionsController
|
class Auth::SessionsController < Devise::SessionsController
|
||||||
include Redisable
|
|
||||||
|
|
||||||
MAX_2FA_ATTEMPTS_PER_HOUR = 10
|
|
||||||
|
|
||||||
layout 'auth'
|
layout 'auth'
|
||||||
|
|
||||||
skip_before_action :check_self_destruct!
|
skip_before_action :check_self_destruct!
|
||||||
|
@ -139,23 +135,9 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
session.delete(:attempt_user_updated_at)
|
session.delete(:attempt_user_updated_at)
|
||||||
end
|
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)
|
def on_authentication_success(user, security_measure)
|
||||||
@on_authentication_success_called = true
|
@on_authentication_success_called = true
|
||||||
|
|
||||||
clear_2fa_attempt_from_user(user)
|
|
||||||
clear_attempt_from_session
|
clear_attempt_from_session
|
||||||
|
|
||||||
user.update_sign_in!(new_sign_in: true)
|
user.update_sign_in!(new_sign_in: true)
|
||||||
|
@ -186,14 +168,5 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
ip: request.remote_ip,
|
ip: request.remote_ip,
|
||||||
user_agent: request.user_agent
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,11 +66,6 @@ module Auth::TwoFactorAuthenticationConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_two_factor_via_otp(user)
|
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)
|
if valid_otp_attempt?(user)
|
||||||
on_authentication_success(user, :otp)
|
on_authentication_success(user, :otp)
|
||||||
else
|
else
|
||||||
|
|
|
@ -22,20 +22,11 @@ module WebAppControllerConcern
|
||||||
def redirect_unauthenticated_to_permalinks!
|
def redirect_unauthenticated_to_permalinks!
|
||||||
return if user_signed_in? # NOTE: Different from upstream because we allow moved users to log in
|
return if user_signed_in? # NOTE: Different from upstream because we allow moved users to log in
|
||||||
|
|
||||||
permalink_redirector = PermalinkRedirector.new(request.path)
|
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||||
return if permalink_redirector.redirect_path.blank?
|
return if redirect_path.blank?
|
||||||
|
|
||||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
|
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
|
end
|
||||||
|
|
||||||
def set_pack
|
def set_pack
|
||||||
|
|
|
@ -1,21 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
||||||
before_action :set_user_roles
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: true
|
expires_in 3.minutes, public: true
|
||||||
render content_type: 'text/css'
|
render content_type: 'text/css'
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,29 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,10 +0,0 @@
|
||||||
# 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 :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :redirect_invalid_otp, unless: -> { current_user.otp_enabled? }
|
before_action :require_otp_enabled
|
||||||
before_action :redirect_invalid_webauthn, only: [:index, :destroy], unless: -> { current_user.webauthn_enabled? }
|
before_action :require_webauthn_enabled, only: [:index, :destroy]
|
||||||
|
|
||||||
def index; end
|
def index; end
|
||||||
def new; end
|
def new; end
|
||||||
|
@ -89,14 +89,18 @@ module Settings
|
||||||
use_pack 'auth'
|
use_pack 'auth'
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_invalid_otp
|
def require_otp_enabled
|
||||||
flash[:error] = t('webauthn_credentials.otp_required')
|
unless current_user.otp_enabled?
|
||||||
redirect_to settings_two_factor_authentication_methods_path
|
flash[:error] = t('webauthn_credentials.otp_required')
|
||||||
|
redirect_to settings_two_factor_authentication_methods_path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_invalid_webauthn
|
def require_webauthn_enabled
|
||||||
flash[:error] = t('webauthn_credentials.not_enabled')
|
unless current_user.webauthn_enabled?
|
||||||
redirect_to settings_two_factor_authentication_methods_path
|
flash[:error] = t('webauthn_credentials.not_enabled')
|
||||||
|
redirect_to settings_two_factor_authentication_methods_path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,26 +31,22 @@ module AccountsHelper
|
||||||
Setting.hide_followers_count || account.user&.settings&.[]('hide_followers_count')
|
Setting.hide_followers_count || account.user&.settings&.[]('hide_followers_count')
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_formatted_stat(value)
|
|
||||||
number_to_human(value, precision: 3, strip_insignificant_zeros: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_description(account)
|
def account_description(account)
|
||||||
prepend_stats = [
|
prepend_stats = [
|
||||||
[
|
[
|
||||||
account_formatted_stat(account.statuses_count),
|
number_to_human(account.statuses_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.posts', count: account.statuses_count),
|
I18n.t('accounts.posts', count: account.statuses_count),
|
||||||
].join(' '),
|
].join(' '),
|
||||||
|
|
||||||
[
|
[
|
||||||
account_formatted_stat(account.following_count),
|
number_to_human(account.following_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.following', count: account.following_count),
|
I18n.t('accounts.following', count: account.following_count),
|
||||||
].join(' '),
|
].join(' '),
|
||||||
]
|
]
|
||||||
|
|
||||||
unless hide_followers_count?(account)
|
unless hide_followers_count?(account)
|
||||||
prepend_stats << [
|
prepend_stats << [
|
||||||
account_formatted_stat(account.followers_count),
|
number_to_human(account.followers_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.followers', count: account.followers_count),
|
I18n.t('accounts.followers', count: account.followers_count),
|
||||||
].join(' ')
|
].join(' ')
|
||||||
end
|
end
|
||||||
|
|
|
@ -155,7 +155,7 @@ module JsonLdHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_resource(uri, id, on_behalf_of = nil, request_options: {})
|
def fetch_resource(uri, id, on_behalf_of = nil)
|
||||||
unless id
|
unless id
|
||||||
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
||||||
|
|
||||||
|
@ -164,14 +164,14 @@ module JsonLdHelper
|
||||||
uri = json['id']
|
uri = json['id']
|
||||||
end
|
end
|
||||||
|
|
||||||
json = fetch_resource_without_id_validation(uri, on_behalf_of, request_options: request_options)
|
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
||||||
json.present? && json['id'] == uri ? json : nil
|
json.present? && json['id'] == uri ? json : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false, request_options: {})
|
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false)
|
||||||
on_behalf_of ||= Account.representative
|
on_behalf_of ||= Account.representative
|
||||||
|
|
||||||
build_request(uri, on_behalf_of, options: request_options).perform do |response|
|
build_request(uri, on_behalf_of).perform do |response|
|
||||||
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error
|
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
|
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))
|
response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code))
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_request(uri, on_behalf_of = nil, options: {})
|
def build_request(uri, on_behalf_of = nil)
|
||||||
Request.new(:get, uri, **options).tap do |request|
|
Request.new(:get, uri).tap do |request|
|
||||||
request.on_behalf_of(on_behalf_of) if on_behalf_of
|
request.on_behalf_of(on_behalf_of) if on_behalf_of
|
||||||
request.add_headers('Accept' => 'application/activity+json, application/ld+json')
|
request.add_headers('Accept' => 'application/activity+json, application/ld+json')
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module MascotHelper
|
module MascotHelper
|
||||||
def mascot_url
|
def mascot_url
|
||||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
full_asset_url(instance_presenter.mascot&.file&.url || asset_pack_path('media/images/elephant_ui_plane.svg'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def instance_presenter
|
def instance_presenter
|
||||||
|
|
|
@ -24,12 +24,8 @@ module RoutingHelper
|
||||||
Rails.configuration.action_controller.asset_host || root_url
|
Rails.configuration.action_controller.asset_host || root_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def frontend_asset_path(source, **options)
|
def full_pack_url(source, **options)
|
||||||
asset_pack_path("media/#{source}", **options)
|
full_asset_url(asset_pack_path(source, **options))
|
||||||
end
|
|
||||||
|
|
||||||
def frontend_asset_url(source, **options)
|
|
||||||
full_asset_url(frontend_asset_path(source, **options))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_storage?
|
def use_storage?
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
const ReactComponent = 'div';
|
// eslint-disable-next-line import/no-anonymous-default-export
|
||||||
|
export default 'SvgrURL';
|
||||||
export default ReactComponent;
|
export const ReactComponent = 'div';
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/* Placeholder file to have `inert.scss` compiled by Webpack
|
|
||||||
This is used by the `wicg-inert` polyfill */
|
|
||||||
|
|
||||||
import '../styles/inert.scss';
|
|
|
@ -10,9 +10,6 @@ pack:
|
||||||
embed: embed.js
|
embed: embed.js
|
||||||
error:
|
error:
|
||||||
home:
|
home:
|
||||||
inert:
|
|
||||||
filename: inert.js
|
|
||||||
stylesheet: true
|
|
||||||
mailer:
|
mailer:
|
||||||
filename: mailer.js
|
filename: mailer.js
|
||||||
stylesheet: true
|
stylesheet: true
|
||||||
|
|
|
@ -170,11 +170,6 @@ export const openURL = routerHistory => (dispatch, getState) => {
|
||||||
|
|
||||||
export const clickSearchResult = (q, type) => (dispatch, getState) => {
|
export const clickSearchResult = (q, type) => (dispatch, getState) => {
|
||||||
const previous = getState().getIn(['search', 'recent']);
|
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 me = getState().getIn(['meta', 'me']);
|
||||||
const current = previous.add(fromJS({ type, q })).takeLast(4);
|
const current = previous.add(fromJS({ type, q })).takeLast(4);
|
||||||
|
|
||||||
|
@ -203,4 +198,4 @@ export const hydrateSearch = () => (dispatch, getState) => {
|
||||||
if (history !== null) {
|
if (history !== null) {
|
||||||
dispatch(updateSearchHistory(history));
|
dispatch(updateSearchHistory(history));
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -17,7 +17,7 @@ import { Avatar } from './avatar';
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
import { FollowersCounter } from './counters';
|
import { FollowersCounter } from './counters';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
import { Permalink } from './permalink';
|
import Permalink from './permalink';
|
||||||
import { RelativeTimestamp } from './relative_timestamp';
|
import { RelativeTimestamp } from './relative_timestamp';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import api from 'flavours/glitch/api';
|
import api from 'flavours/glitch/api';
|
||||||
import { Hashtag } from 'flavours/glitch/components/hashtag';
|
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||||
|
|
||||||
export default class Trends extends PureComponent {
|
export default class Trends extends PureComponent {
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
|
||||||
|
|
||||||
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import GroupsIcon from '@/material-icons/400-24px/group.svg?react';
|
import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||||
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
|
import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg';
|
||||||
|
|
||||||
|
|
||||||
export const Badge = ({ icon, label, domain }) => (
|
export const Badge = ({ icon, label, domain }) => (
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { useCallback } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react';
|
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context';
|
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 classNames from 'classnames';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||||
import ArrowBackIcon from '@/material-icons/400-24px/arrow_back.svg?react';
|
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||||
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg';
|
||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||||
import TuneIcon from '@/material-icons/400-24px/tune.svg?react';
|
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { ButtonInTabsBar, useColumnsContext } from 'flavours/glitch/features/ui/util/columns_context';
|
import { ButtonInTabsBar, useColumnsContext } from 'flavours/glitch/features/ui/util/columns_context';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
|
|
||||||
import { useAppHistory } from './router';
|
import { useAppHistory } from './router';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { useCallback, useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||||
|
|
||||||
import { changeSetting } from 'flavours/glitch/actions/settings';
|
import { changeSetting } from 'flavours/glitch/actions/settings';
|
||||||
import { bannerSettings } from 'flavours/glitch/settings';
|
import { bannerSettings } from 'flavours/glitch/settings';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
||||||
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
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 { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import Overlay from 'react-overlays/Overlay';
|
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 { CircularProgress } from 'flavours/glitch/components/circular_progress';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { openModal } from 'flavours/glitch/actions/modal';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import InlineAccount from 'flavours/glitch/components/inline_account';
|
import InlineAccount from 'flavours/glitch/components/inline_account';
|
||||||
|
|
123
app/javascript/flavours/glitch/components/hashtag.jsx
Normal file
123
app/javascript/flavours/glitch/components/hashtag.jsx
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// @ts-check
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Component } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||||
|
|
||||||
|
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||||
|
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||||
|
|
||||||
|
import Permalink from './permalink';
|
||||||
|
|
||||||
|
class SilentErrorBoundary extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
error: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidCatch() {
|
||||||
|
this.setState({ error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to render counter of how much people are talking about hashtag
|
||||||
|
* @type {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element}
|
||||||
|
*/
|
||||||
|
export const accountsCountRenderer = (displayNumber, pluralReady) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='trends.counter_by_accounts'
|
||||||
|
defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
days: 2,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
export const ImmutableHashtag = ({ hashtag }) => (
|
||||||
|
<Hashtag
|
||||||
|
name={hashtag.get('name')}
|
||||||
|
href={hashtag.get('url')}
|
||||||
|
to={`/tags/${hashtag.get('name')}`}
|
||||||
|
people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
|
||||||
|
// @ts-expect-error
|
||||||
|
history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
ImmutableHashtag.propTypes = {
|
||||||
|
hashtag: ImmutablePropTypes.map.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
const Hashtag = ({ name, href, to, people, uses, history, className, description, withGraph }) => (
|
||||||
|
<div className={classNames('trends__item', className)}>
|
||||||
|
<div className='trends__item__name'>
|
||||||
|
<Permalink href={href} to={to}>
|
||||||
|
{name ? <>#<span>{name}</span></> : <Skeleton width={50} />}
|
||||||
|
</Permalink>
|
||||||
|
|
||||||
|
{description ? (
|
||||||
|
<span>{description}</span>
|
||||||
|
) : (
|
||||||
|
typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{typeof uses !== 'undefined' && (
|
||||||
|
<div className='trends__item__current'>
|
||||||
|
<ShortNumber value={uses} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{withGraph && (
|
||||||
|
<div className='trends__item__sparkline'>
|
||||||
|
<SilentErrorBoundary>
|
||||||
|
<Sparklines width={50} height={28} data={history ? history : Array.from(Array(7)).map(() => 0)}>
|
||||||
|
<SparklinesCurve style={{ fill: 'none' }} />
|
||||||
|
</Sparklines>
|
||||||
|
</SilentErrorBoundary>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
Hashtag.propTypes = {
|
||||||
|
name: PropTypes.string,
|
||||||
|
href: PropTypes.string,
|
||||||
|
to: PropTypes.string,
|
||||||
|
people: PropTypes.number,
|
||||||
|
description: PropTypes.node,
|
||||||
|
uses: PropTypes.number,
|
||||||
|
history: PropTypes.arrayOf(PropTypes.number),
|
||||||
|
className: PropTypes.string,
|
||||||
|
withGraph: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
Hashtag.defaultProps = {
|
||||||
|
withGraph: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Hashtag;
|
|
@ -1,149 +0,0 @@
|
||||||
import type { JSX } from 'react';
|
|
||||||
import { Component } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import type Immutable from 'immutable';
|
|
||||||
|
|
||||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
|
||||||
|
|
||||||
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
|
||||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
|
||||||
|
|
||||||
import { Permalink } from './permalink';
|
|
||||||
|
|
||||||
interface SilentErrorBoundaryProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SilentErrorBoundary extends Component<SilentErrorBoundaryProps> {
|
|
||||||
state = {
|
|
||||||
error: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidCatch() {
|
|
||||||
this.setState({ error: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.error) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.props.children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to render counter of how much people are talking about hashtag
|
|
||||||
* @param displayNumber Counter number to display
|
|
||||||
* @param pluralReady Whether the count is plural
|
|
||||||
* @returns Formatted counter of how much people are talking about hashtag
|
|
||||||
*/
|
|
||||||
export const accountsCountRenderer = (
|
|
||||||
displayNumber: JSX.Element,
|
|
||||||
pluralReady: number,
|
|
||||||
) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='trends.counter_by_accounts'
|
|
||||||
defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: <strong>{displayNumber}</strong>,
|
|
||||||
days: 2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
interface ImmutableHashtagProps {
|
|
||||||
hashtag: Immutable.Map<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ImmutableHashtag = ({ hashtag }: ImmutableHashtagProps) => (
|
|
||||||
<Hashtag
|
|
||||||
name={hashtag.get('name') as string}
|
|
||||||
href={hashtag.get('url') as string}
|
|
||||||
to={`/tags/${hashtag.get('name') as string}`}
|
|
||||||
people={
|
|
||||||
(hashtag.getIn(['history', 0, 'accounts']) as number) * 1 +
|
|
||||||
(hashtag.getIn(['history', 1, 'accounts']) as number) * 1
|
|
||||||
}
|
|
||||||
history={(
|
|
||||||
hashtag.get('history') as Immutable.Collection.Indexed<
|
|
||||||
Immutable.Map<string, number>
|
|
||||||
>
|
|
||||||
)
|
|
||||||
.reverse()
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
.map((day) => day.get('uses')!)
|
|
||||||
.toArray()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export interface HashtagProps {
|
|
||||||
className?: string;
|
|
||||||
description?: React.ReactNode;
|
|
||||||
history?: number[];
|
|
||||||
href: string;
|
|
||||||
name: string;
|
|
||||||
people: number;
|
|
||||||
to: string;
|
|
||||||
uses?: number;
|
|
||||||
withGraph?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Hashtag: React.FC<HashtagProps> = ({
|
|
||||||
name,
|
|
||||||
href,
|
|
||||||
to,
|
|
||||||
people,
|
|
||||||
uses,
|
|
||||||
history,
|
|
||||||
className,
|
|
||||||
description,
|
|
||||||
withGraph = true,
|
|
||||||
}) => (
|
|
||||||
<div className={classNames('trends__item', className)}>
|
|
||||||
<div className='trends__item__name'>
|
|
||||||
<Permalink href={href} to={to}>
|
|
||||||
{name ? (
|
|
||||||
<>
|
|
||||||
#<span>{name}</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton width={50} />
|
|
||||||
)}
|
|
||||||
</Permalink>
|
|
||||||
|
|
||||||
{description ? (
|
|
||||||
<span>{description}</span>
|
|
||||||
) : typeof people !== 'undefined' ? (
|
|
||||||
<ShortNumber value={people} renderer={accountsCountRenderer} />
|
|
||||||
) : (
|
|
||||||
<Skeleton width={100} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{typeof uses !== 'undefined' && (
|
|
||||||
<div className='trends__item__current'>
|
|
||||||
<ShortNumber value={uses} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{withGraph && (
|
|
||||||
<div className='trends__item__sparkline'>
|
|
||||||
<SilentErrorBoundary>
|
|
||||||
<Sparklines
|
|
||||||
width={50}
|
|
||||||
height={28}
|
|
||||||
data={history ? history : Array.from(Array(7)).map(() => 0)}
|
|
||||||
>
|
|
||||||
<SparklinesCurve style={{ fill: 'none' }} />
|
|
||||||
</Sparklines>
|
|
||||||
</SilentErrorBoundary>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
|
@ -1,7 +1,6 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import CheckBoxOutlineBlankIcon from '@/material-icons/400-24px/check_box_outline_blank.svg?react';
|
import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
|
||||||
import { isProduction } from 'flavours/glitch/utils/environment';
|
|
||||||
|
|
||||||
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
|
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -25,7 +24,7 @@ export const Icon: React.FC<Props> = ({
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (!IconComponent) {
|
if (!IconComponent) {
|
||||||
if (!isProduction()) {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`<Icon id="${id}" className="${className}"> is missing an "icon" prop.`,
|
`<Icon id="${id}" className="${className}"> is missing an "icon" prop.`,
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import logo from '@/images/logo.svg';
|
import logo from 'mastodon/../images/logo.svg';
|
||||||
|
|
||||||
export const WordmarkLogo = () => (
|
export const WordmarkLogo = () => (
|
||||||
<svg viewBox='0 0 261 66' className='logo logo--wordmark' role='img'>
|
<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 { is } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
|
||||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||||
|
|
||||||
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
|
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
|
||||||
|
@ -123,7 +123,7 @@ class Item extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment.get('description')?.length > 0) {
|
if (attachment.get('description')?.length > 0) {
|
||||||
badges.push(<span key='alt' className='media-gallery__alt__label'>ALT</span>);
|
badges.push(<span key='alt' className='media-gallery__gifv__label'>ALT</span>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||||
|
|
|
@ -12,9 +12,9 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
|
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
|
||||||
|
|
||||||
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' },
|
btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' },
|
||||||
|
|
50
app/javascript/flavours/glitch/components/permalink.jsx
Normal file
50
app/javascript/flavours/glitch/components/permalink.jsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
|
class Permalink extends PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
href: PropTypes.string.isRequired,
|
||||||
|
to: PropTypes.string.isRequired,
|
||||||
|
children: PropTypes.node,
|
||||||
|
onInterceptClick: PropTypes.func,
|
||||||
|
...WithOptionalRouterPropTypes,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = (e) => {
|
||||||
|
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
|
if (this.props.onInterceptClick && this.props.onInterceptClick()) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.history) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.history.push(this.props.to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
href,
|
||||||
|
to,
|
||||||
|
onInterceptClick,
|
||||||
|
...other
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withOptionalRouter(Permalink);
|
|
@ -1,41 +0,0 @@
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { useAppHistory } from './router';
|
|
||||||
|
|
||||||
interface Props extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
||||||
to: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Permalink: React.FC<Props> = ({
|
|
||||||
className,
|
|
||||||
href,
|
|
||||||
to,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const history = useAppHistory();
|
|
||||||
|
|
||||||
const handleClick = useCallback<React.MouseEventHandler<HTMLAnchorElement>>(
|
|
||||||
(e) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- history can actually be undefined as the component can be mounted outside a router context
|
|
||||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey) && history) {
|
|
||||||
e.preventDefault();
|
|
||||||
history.push(to);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[history, to],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
target='_blank'
|
|
||||||
rel='noreferrer'
|
|
||||||
href={href}
|
|
||||||
onClick={handleClick}
|
|
||||||
className={`permalink${className ? ' ' + className : ''}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -5,8 +5,8 @@ import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { removePictureInPicture } from 'flavours/glitch/actions/picture_in_picture';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 escapeTextContentForBrowser from 'escape-html';
|
||||||
import spring from 'react-motion/lib/spring';
|
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 { Icon } from 'flavours/glitch/components/icon';
|
||||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||||
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
||||||
|
|
|
@ -11,7 +11,6 @@ import type {
|
||||||
import { createBrowserHistory } from 'history';
|
import { createBrowserHistory } from 'history';
|
||||||
|
|
||||||
import { layoutFromWindow } from 'flavours/glitch/is_mobile';
|
import { layoutFromWindow } from 'flavours/glitch/is_mobile';
|
||||||
import { isDevelopment } from 'flavours/glitch/utils/environment';
|
|
||||||
|
|
||||||
interface MastodonLocationState {
|
interface MastodonLocationState {
|
||||||
fromMastodon?: boolean;
|
fromMastodon?: boolean;
|
||||||
|
@ -41,7 +40,7 @@ function normalizePath(
|
||||||
} else if (
|
} else if (
|
||||||
location.state !== undefined &&
|
location.state !== undefined &&
|
||||||
state !== undefined &&
|
state !== undefined &&
|
||||||
isDevelopment()
|
process.env.NODE_ENV === 'development'
|
||||||
) {
|
) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(
|
console.log(
|
||||||
|
|
|
@ -8,22 +8,21 @@ import { withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
|
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||||
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
|
import { ReactComponent as BookmarkBorderIcon } from '@material-symbols/svg-600/outlined/bookmark.svg';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
|
||||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg';
|
||||||
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
|
import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg';
|
||||||
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
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 DropdownMenuContainer from '../containers/dropdown_menu_container';
|
||||||
import { me } from '../initial_state';
|
import { me } from '../initial_state';
|
||||||
|
@ -305,7 +304,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (status.get('reblogged')) {
|
if (status.get('reblogged')) {
|
||||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||||
} else if (publicStatus) {
|
} else if (publicStatus) {
|
||||||
reblogTitle = intl.formatMessage(messages.reblog);
|
reblogTitle = intl.formatMessage(messages.reblog);
|
||||||
reblogIconComponent = RepeatIcon;
|
reblogIconComponent = RepeatIcon;
|
||||||
|
|
|
@ -9,17 +9,17 @@ import { withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
|
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
|
||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
|
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
|
||||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
|
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||||
|
|
||||||
|
import Permalink from './permalink';
|
||||||
import { Permalink } from './permalink';
|
|
||||||
|
|
||||||
const textMatchesTarget = (text, origin, host) => {
|
const textMatchesTarget = (text, origin, host) => {
|
||||||
return (text === origin || text === host
|
return (text === origin || text === host
|
||||||
|
|
|
@ -6,18 +6,18 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import ExpandLessIcon from '@/material-icons/400-24px/expand_less.svg?react';
|
import { ReactComponent as ExpandLessIcon } from '@material-symbols/svg-600/outlined/expand_less.svg';
|
||||||
import ForumIcon from '@/material-icons/400-24px/forum.svg?react';
|
import { ReactComponent as ForumIcon } from '@material-symbols/svg-600/outlined/forum.svg';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home.svg';
|
||||||
import ImageIcon from '@/material-icons/400-24px/image.svg?react';
|
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
|
||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||||
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
|
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||||
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
|
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
|
||||||
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
|
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { languages } from 'flavours/glitch/initial_state';
|
import { languages } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
import { VisibilityIcon } from './visibility_icon';
|
import { VisibilityIcon } from './visibility_icon';
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,16 @@ import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
|
||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||||
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg';
|
||||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { me } from 'flavours/glitch/initial_state';
|
import { me } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|
||||||
export default class StatusPrepend extends PureComponent {
|
export default class StatusPrepend extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||||
|
|
||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||||
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
|
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||||
|
|
||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,8 @@ import UI from 'flavours/glitch/features/ui';
|
||||||
import initialState, { title as siteTitle } from 'flavours/glitch/initial_state';
|
import initialState, { title as siteTitle } from 'flavours/glitch/initial_state';
|
||||||
import { IntlProvider } from 'flavours/glitch/locales';
|
import { IntlProvider } from 'flavours/glitch/locales';
|
||||||
import { store } from 'flavours/glitch/store';
|
import { store } from 'flavours/glitch/store';
|
||||||
import { isProduction } from 'flavours/glitch/utils/environment';
|
|
||||||
|
|
||||||
const title = isProduction() ? siteTitle : `${siteTitle} (Dev)`;
|
const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
|
||||||
|
|
||||||
const hydrateAction = hydrateStore(initialState);
|
const hydrateAction = hydrateStore(initialState);
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ import { List as ImmutableList } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
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 { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server';
|
||||||
import Column from 'flavours/glitch/components/column';
|
import Column from 'flavours/glitch/components/column';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
|
||||||
|
|
||||||
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
class ActionBar extends PureComponent {
|
class ActionBar extends PureComponent {
|
||||||
|
|
||||||
|
@ -30,8 +30,7 @@ class ActionBar extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='account__disclaimer'>
|
<div className='account__disclaimer'>
|
||||||
<Icon id='info-circle' icon={InfoIcon} />
|
<Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
|
||||||
<FormattedMessage
|
|
||||||
id='account.suspended_disclaimer_full'
|
id='account.suspended_disclaimer_full'
|
||||||
defaultMessage='This user has been suspended by a moderator.'
|
defaultMessage='This user has been suspended by a moderator.'
|
||||||
/>
|
/>
|
||||||
|
@ -45,17 +44,14 @@ class ActionBar extends PureComponent {
|
||||||
if (account.get('acct') !== account.get('username')) {
|
if (account.get('acct') !== account.get('username')) {
|
||||||
extraInfo = (
|
extraInfo = (
|
||||||
<div className='account__disclaimer'>
|
<div className='account__disclaimer'>
|
||||||
<Icon id='info-circle' icon={InfoIcon} />
|
<Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
|
||||||
<div>
|
id='account.disclaimer_full'
|
||||||
<FormattedMessage
|
defaultMessage="Information below may reflect the user's profile incompletely."
|
||||||
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 target='_blank' rel='noopener' href={account.get('url')}>
|
</a>
|
||||||
<FormattedMessage id='account.view_full_profile' defaultMessage='View full profile' />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Hashtag } from 'flavours/glitch/components/hashtag';
|
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' },
|
lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' },
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
|
||||||
|
|
||||||
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
|
|
||||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,12 @@ import { withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||||
|
|
||||||
import ColumnHeader from '../../../components/column_header';
|
import ColumnHeader from '../../../components/column_header';
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import AudiotrackIcon from '@/material-icons/400-24px/music_note.svg?react';
|
import { ReactComponent as AudiotrackIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||||
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow.svg?react';
|
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg';
|
||||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||||
|
|
||||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
|
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|
||||||
export default class MediaItem extends ImmutablePureComponent {
|
export default class MediaItem extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,17 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import { is } from 'immutable';
|
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 { 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 { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
|
import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
|
||||||
|
|
||||||
|
|
||||||
import { Blurhash } from '../../components/blurhash';
|
import { Blurhash } from '../../components/blurhash';
|
||||||
import { displayMedia, useBlurhash } from '../../initial_state';
|
import { displayMedia, useBlurhash } from '../../initial_state';
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
|
||||||
|
|
||||||
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
||||||
import { LoadingIndicator } from '../../components/loading_indicator';
|
import { LoadingIndicator } from '../../components/loading_indicator';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
|
|
@ -8,9 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
|
||||||
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks';
|
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks';
|
||||||
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
|
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
|
||||||
import ColumnHeader from 'flavours/glitch/components/column_header';
|
import ColumnHeader from 'flavours/glitch/components/column_header';
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||||
import { domain } from 'flavours/glitch/initial_state';
|
import { domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
|
||||||
import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links';
|
|
||||||
|
|
||||||
|
import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|
||||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,18 @@ import { Link } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
|
||||||
import LogoutIcon from '@/material-icons/400-24px/logout.svg?react';
|
import { ReactComponent as LogoutIcon } from '@material-symbols/svg-600/outlined/logout.svg';
|
||||||
import ManufacturingIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
import { ReactComponent as ManufacturingIcon } from '@material-symbols/svg-600/outlined/manufacturing.svg';
|
||||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
|
||||||
import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg';
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||||
|
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { signOutLink } from 'flavours/glitch/utils/backend_links';
|
import { signOutLink } from 'flavours/glitch/utils/backend_links';
|
||||||
import { conditionalRender } from 'flavours/glitch/utils/react_helpers';
|
import { conditionalRender } from 'flavours/glitch/utils/react_helpers';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
community: {
|
community: {
|
||||||
defaultMessage: 'Local timeline',
|
defaultMessage: 'Local timeline',
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Permalink } from 'flavours/glitch/components/permalink';
|
import Permalink from 'flavours/glitch/components/permalink';
|
||||||
import { profileLink } from 'flavours/glitch/utils/backend_links';
|
import { profileLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '../../../components/avatar';
|
||||||
|
|
|
@ -6,20 +6,19 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 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 { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
import { pollLimits } from 'flavours/glitch/initial_state';
|
import { pollLimits } from 'flavours/glitch/initial_state';
|
||||||
|
|
||||||
|
|
||||||
import DropdownContainer from '../containers/dropdown_container';
|
import DropdownContainer from '../containers/dropdown_container';
|
||||||
import LanguageDropdown from '../containers/language_dropdown_container';
|
import LanguageDropdown from '../containers/language_dropdown_container';
|
||||||
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
||||||
|
|
|
@ -8,9 +8,9 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 AutosuggestInput from 'flavours/glitch/components/autosuggest_input';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||||
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
|
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||||
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
|
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||||
|
|
||||||
import Dropdown from './dropdown';
|
import Dropdown from './dropdown';
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue