mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2024-11-25 14:28:07 +01:00
Merge commit '3a191b3797dde1daf79cd748a14b87240532d543' into glitch-soc/merge-upstream
This commit is contained in:
commit
677f73f793
61 changed files with 598 additions and 477 deletions
|
@ -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
|
|
@ -366,6 +366,9 @@ module.exports = defineConfig({
|
|||
// Disable formatting rules that have been enabled in the base config
|
||||
'indent': 'off',
|
||||
|
||||
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
|
||||
'consistent-return': 'off',
|
||||
|
||||
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
|
|
13
.github/workflows/bundler-audit.yml
vendored
13
.github/workflows/bundler-audit.yml
vendored
|
@ -6,14 +6,12 @@ on:
|
|||
paths:
|
||||
- 'Gemfile*'
|
||||
- '.ruby-version'
|
||||
- '.bundler-audit.yml'
|
||||
- '.github/workflows/bundler-audit.yml'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'Gemfile*'
|
||||
- '.ruby-version'
|
||||
- '.bundler-audit.yml'
|
||||
- '.github/workflows/bundler-audit.yml'
|
||||
|
||||
schedule:
|
||||
|
@ -23,12 +21,17 @@ jobs:
|
|||
security:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
BUNDLE_ONLY: development
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Run bundler-audit
|
||||
run: bundle exec bundler-audit
|
||||
run: bundle exec bundler-audit check --update
|
||||
|
|
10
.github/workflows/lint-haml.yml
vendored
10
.github/workflows/lint-haml.yml
vendored
|
@ -26,12 +26,18 @@ on:
|
|||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
BUNDLE_ONLY: development
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Run haml-lint
|
||||
run: |
|
||||
|
|
11
.github/workflows/lint-ruby.yml
vendored
11
.github/workflows/lint-ruby.yml
vendored
|
@ -27,18 +27,23 @@ jobs:
|
|||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
BUNDLE_ONLY: development
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Set-up RuboCop Problem Matcher
|
||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||
|
||||
- name: Run rubocop
|
||||
run: bundle exec rubocop
|
||||
run: bin/rubocop
|
||||
|
||||
- name: Run brakeman
|
||||
if: always() # Run both checks, even if the first failed
|
||||
|
|
95
.github/workflows/test-migrations-two-step.yml
vendored
95
.github/workflows/test-migrations-two-step.yml
vendored
|
@ -1,95 +0,0 @@
|
|||
name: Test two step migrations
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
- 'renovate/**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations-two-step.yml", "lib/tasks/tests.rake"]'
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
postgres:
|
||||
- 14-alpine
|
||||
- 15-alpine
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:${{ matrix.postgres}}
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
env:
|
||||
CONTINUOUS_INTEGRATION: true
|
||||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: true
|
||||
RAILS_ENV: test
|
||||
BUNDLE_CLEAN: true
|
||||
BUNDLE_FROZEN: true
|
||||
BUNDLE_WITHOUT: 'development production'
|
||||
BUNDLE_JOBS: 3
|
||||
BUNDLE_RETRY: 3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Run all remaining pre-deployment migrations
|
||||
run: './bin/rails db:migrate'
|
||||
env:
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
||||
|
||||
- name: Run all post-deployment migrations
|
||||
run: './bin/rails db:migrate'
|
||||
|
||||
- name: Check migration result
|
||||
run: './bin/rails tests:migrations:check_database'
|
|
@ -1,4 +1,5 @@
|
|||
name: Test one step migrations
|
||||
name: Historical data migration test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
|
@ -17,7 +18,7 @@ jobs:
|
|||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations-one-step.yml", "lib/tasks/tests.rake"]'
|
||||
paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations.yml", "lib/tasks/tests.rake"]'
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -57,7 +58,6 @@ jobs:
|
|||
- 6379:6379
|
||||
|
||||
env:
|
||||
CONTINUOUS_INTEGRATION: true
|
||||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
|
@ -65,7 +65,7 @@ jobs:
|
|||
RAILS_ENV: test
|
||||
BUNDLE_CLEAN: true
|
||||
BUNDLE_FROZEN: true
|
||||
BUNDLE_WITHOUT: 'development production'
|
||||
BUNDLE_WITHOUT: 'development:production'
|
||||
BUNDLE_JOBS: 3
|
||||
BUNDLE_RETRY: 3
|
||||
|
||||
|
@ -75,14 +75,19 @@ jobs:
|
|||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
- name: Test "one step migration" flow
|
||||
run: |
|
||||
bin/rails db:drop
|
||||
bin/rails db:create
|
||||
bin/rails tests:migrations:prepare_database
|
||||
bin/rails db:migrate
|
||||
bin/rails tests:migrations:check_database
|
||||
|
||||
- name: Run historical migrations with data population
|
||||
run: './bin/rails tests:migrations:prepare_database'
|
||||
|
||||
- name: Run all remaining migrations
|
||||
run: './bin/rails db:migrate'
|
||||
|
||||
- name: Check migration result
|
||||
run: './bin/rails tests:migrations:check_database'
|
||||
- name: Test "two step migration" flow
|
||||
run: |
|
||||
bin/rails db:drop
|
||||
bin/rails db:create
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
|
||||
bin/rails db:migrate
|
||||
bin/rails tests:migrations:check_database
|
259
.rubocop.yml
259
.rubocop.yml
|
@ -1,7 +1,34 @@
|
|||
# Can be removed once all rules are addressed or moved to this file as documented overrides
|
||||
inherit_from: .rubocop_todo.yml
|
||||
---
|
||||
AllCops:
|
||||
CacheRootDirectory: tmp
|
||||
DisplayCopNames: true
|
||||
DisplayStyleGuide: true
|
||||
Exclude:
|
||||
- db/schema.rb
|
||||
- bin/*
|
||||
- node_modules/**/*
|
||||
- Vagrantfile
|
||||
- vendor/**/*
|
||||
- config/initializers/json_ld*
|
||||
- lib/mastodon/migration_helpers.rb
|
||||
- lib/templates/**/*
|
||||
ExtraDetails: true
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.1 # Oldest supported ruby version
|
||||
UseCache: true
|
||||
|
||||
inherit_from:
|
||||
- .rubocop/layout.yml
|
||||
- .rubocop/metrics.yml
|
||||
- .rubocop/naming.yml
|
||||
- .rubocop/rails.yml
|
||||
- .rubocop/rspec_rails.yml
|
||||
- .rubocop/rspec.yml
|
||||
- .rubocop/style.yml
|
||||
- .rubocop/custom.yml
|
||||
- .rubocop_todo.yml
|
||||
- .rubocop/strict.yml
|
||||
|
||||
# Used for merging with exclude lists with .rubocop_todo.yml
|
||||
inherit_mode:
|
||||
merge:
|
||||
- Exclude
|
||||
|
@ -12,229 +39,3 @@ require:
|
|||
- rubocop-rspec_rails
|
||||
- rubocop-performance
|
||||
- rubocop-capybara
|
||||
- ./lib/linter/rubocop_middle_dot
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.1 # Set to minimum supported version of CI
|
||||
DisplayCopNames: true
|
||||
DisplayStyleGuide: true
|
||||
ExtraDetails: true
|
||||
UseCache: true
|
||||
CacheRootDirectory: tmp
|
||||
NewCops: enable # Opt-in to newly added rules
|
||||
Exclude:
|
||||
- db/schema.rb
|
||||
- 'bin/*'
|
||||
- 'node_modules/**/*'
|
||||
- 'Vagrantfile'
|
||||
- 'vendor/**/*'
|
||||
- 'config/initializers/json_ld*' # Generated files
|
||||
- 'lib/mastodon/migration_helpers.rb' # Vendored from GitLab
|
||||
- 'lib/templates/**/*'
|
||||
|
||||
# Reason: Prefer Hashes without extreme indentation
|
||||
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutfirsthashelementindentation
|
||||
Layout/FirstHashElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
|
||||
# Reason: Currently disabled in .rubocop_todo.yml
|
||||
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
|
||||
Layout/LineLength:
|
||||
Max: 300 # Default of 120 causes a duplicate entry in generated todo file
|
||||
|
||||
## Disable most Metrics/*Length cops
|
||||
# Reason: those are often triggered and force significant refactors when this happend
|
||||
# but the team feel they are not really improving the code quality.
|
||||
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
|
||||
Metrics/ModuleLength:
|
||||
Enabled: false
|
||||
|
||||
## End Disable Metrics/*Length cops
|
||||
|
||||
# Reason: Currently disabled in .rubocop_todo.yml
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
|
||||
Metrics/AbcSize:
|
||||
Exclude:
|
||||
- 'lib/mastodon/cli/*.rb'
|
||||
|
||||
# Reason: Currently disabled in .rubocop_todo.yml
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
||||
|
||||
# Reason: Prefer seeing a variable name
|
||||
# https://docs.rubocop.org/rubocop/cops_naming.html#namingblockforwarding
|
||||
Naming/BlockForwarding:
|
||||
EnforcedStyle: explicit
|
||||
|
||||
# Reason: Prevailing style is argument file paths
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
|
||||
Rails/FilePath:
|
||||
EnforcedStyle: arguments
|
||||
|
||||
# Reason: Prevailing style uses numeric status codes, matches RSpec/Rails/HttpStatus
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railshttpstatus
|
||||
Rails/HttpStatus:
|
||||
EnforcedStyle: numeric
|
||||
|
||||
# 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:
|
||||
Enabled: false
|
||||
|
||||
# Reason: We want to preserve the ability to migrate from arbitrary old versions,
|
||||
# and cannot guarantee that every installation has run every migration as they upgrade.
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsunusedignoredcolumns
|
||||
Rails/UnusedIgnoredColumns:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Prevailing style choice
|
||||
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsnegateinclude
|
||||
Rails/NegateInclude:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Enforce default limit, but allow some elements to span lines
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecexamplelength
|
||||
RSpec/ExampleLength:
|
||||
CountAsOne: ['array', 'heredoc', 'method_call']
|
||||
|
||||
# Reason: Deprecated cop, will be removed in 3.0, replaced by SpecFilePathFormat
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
|
||||
RSpec/FilePath:
|
||||
Enabled: false
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
|
||||
RSpec/NamedSubject:
|
||||
EnforcedStyle: named_only
|
||||
|
||||
# Reason: Prevailing style choice
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnottonot
|
||||
RSpec/NotToNot:
|
||||
EnforcedStyle: to_not
|
||||
|
||||
# Reason: Match overrides from Rspec/FilePath rule above
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecspecfilepathformat
|
||||
RSpec/SpecFilePathFormat:
|
||||
CustomTransform:
|
||||
ActivityPub: activitypub
|
||||
DeepL: deepl
|
||||
FetchOEmbedService: fetch_oembed_service
|
||||
OEmbedController: oembed_controller
|
||||
OStatus: ostatus
|
||||
|
||||
# Reason: Prevailing style uses numeric status codes, matches Rails/HttpStatus
|
||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec_rails.html#rspecrailshttpstatus
|
||||
RSpecRails/HttpStatus:
|
||||
EnforcedStyle: numeric
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleclassandmodulechildren
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Classes mostly self-document with their names
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styledocumentation
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Route redirects are not token-formatted and must be skipped
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleformatstringtoken
|
||||
Style/FormatStringToken:
|
||||
inherit_mode:
|
||||
merge:
|
||||
- AllowedMethods # The rubocop-rails config adds `redirect`
|
||||
AllowedMethods:
|
||||
- redirect_with_vary
|
||||
|
||||
# Reason: Prevailing style choice
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashaslastarrayitem
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Enforce modern Ruby style
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
|
||||
Style/HashSyntax:
|
||||
EnforcedStyle: ruby19_no_mixed_keys
|
||||
EnforcedShorthandSyntax: either
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylenumericliterals
|
||||
Style/NumericLiterals:
|
||||
AllowedPatterns:
|
||||
- \d{4}_\d{2}_\d{2}_\d{6} # For DB migration date version number readability
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylepercentliteraldelimiters
|
||||
Style/PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
'%i': '()'
|
||||
'%w': '()'
|
||||
|
||||
# Reason: Prefer less indentation in conditional assignments
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantbegin
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Prevailing style choice
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
|
||||
Style/RedundantFetchBlock:
|
||||
Enabled: false
|
||||
|
||||
# Reason: Overridden to reduce implicit StandardError rescues
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
|
||||
Style/RescueStandardError:
|
||||
EnforcedStyle: implicit
|
||||
|
||||
# Reason: Originally disabled for CodeClimate, and no config consensus has been found
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainarrayliteral
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
||||
# Reason:
|
||||
# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
||||
Style/MiddleDot:
|
||||
Enabled: true
|
||||
|
|
6
.rubocop/custom.yml
Normal file
6
.rubocop/custom.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
require:
|
||||
- ../lib/linter/rubocop_middle_dot
|
||||
|
||||
Style/MiddleDot:
|
||||
Enabled: true
|
6
.rubocop/layout.yml
Normal file
6
.rubocop/layout.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
Layout/FirstHashElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 300 # Default of 120 causes a duplicate entry in generated todo file
|
23
.rubocop/metrics.yml
Normal file
23
.rubocop/metrics.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
Metrics/AbcSize:
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ModuleLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
3
.rubocop/naming.yml
Normal file
3
.rubocop/naming.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
Naming/BlockForwarding:
|
||||
EnforcedStyle: explicit
|
27
.rubocop/rails.yml
Normal file
27
.rubocop/rails.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
Rails/FilePath:
|
||||
EnforcedStyle: arguments
|
||||
|
||||
Rails/HttpStatus:
|
||||
EnforcedStyle: numeric
|
||||
|
||||
Rails/LexicallyScopedActionFilter:
|
||||
Exclude:
|
||||
- app/controllers/auth/* # Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
|
||||
|
||||
Rails/NegateInclude:
|
||||
Enabled: false
|
||||
|
||||
Rails/RakeEnvironment:
|
||||
Exclude: # Tasks are doing local work which do not need full env loaded
|
||||
- lib/tasks/auto_annotate_models.rake
|
||||
- lib/tasks/emojis.rake
|
||||
- lib/tasks/mastodon.rake
|
||||
- lib/tasks/repo.rake
|
||||
- lib/tasks/statistics.rake
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Rails/UnusedIgnoredColumns:
|
||||
Enabled: false # Preserve ability to migrate from arbitrary old versions
|
17
.rubocop/rspec.yml
Normal file
17
.rubocop/rspec.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
RSpec/ExampleLength:
|
||||
CountAsOne: ['array', 'heredoc', 'method_call']
|
||||
|
||||
RSpec/NamedSubject:
|
||||
EnforcedStyle: named_only
|
||||
|
||||
RSpec/NotToNot:
|
||||
EnforcedStyle: to_not
|
||||
|
||||
RSpec/SpecFilePathFormat:
|
||||
CustomTransform:
|
||||
ActivityPub: activitypub
|
||||
DeepL: deepl
|
||||
FetchOEmbedService: fetch_oembed_service
|
||||
OEmbedController: oembed_controller
|
||||
OStatus: ostatus
|
3
.rubocop/rspec_rails.yml
Normal file
3
.rubocop/rspec_rails.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
RSpecRails/HttpStatus:
|
||||
EnforcedStyle: numeric
|
19
.rubocop/strict.yml
Normal file
19
.rubocop/strict.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
Lint/Debugger: # Remove any `binding.pry`
|
||||
Enabled: true
|
||||
Exclude: []
|
||||
|
||||
RSpec/Focus: # Require full spec run on CI
|
||||
Enabled: true
|
||||
Exclude: []
|
||||
|
||||
Rails/Output: # Remove any `puts` debugging
|
||||
Enabled: true
|
||||
Exclude: []
|
||||
|
||||
Rails/FindEach: # Using `each` could impact performance, use `find_each`
|
||||
Enabled: true
|
||||
Exclude: []
|
||||
|
||||
Rails/UniqBeforePluck: # Require `uniq.pluck` and not `pluck.uniq`
|
||||
Enabled: true
|
||||
Exclude: []
|
47
.rubocop/style.yml
Normal file
47
.rubocop/style.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Style/FormatStringToken:
|
||||
AllowedMethods:
|
||||
- redirect_with_vary # Route redirects are not token-formatted
|
||||
inherit_mode:
|
||||
merge:
|
||||
- AllowedMethods
|
||||
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
Style/HashSyntax:
|
||||
EnforcedShorthandSyntax: either
|
||||
EnforcedStyle: ruby19_no_mixed_keys
|
||||
|
||||
Style/NumericLiterals:
|
||||
AllowedPatterns:
|
||||
- \d{4}_\d{2}_\d{2}_\d{6}
|
||||
|
||||
Style/PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
'%i': ()
|
||||
'%w': ()
|
||||
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantFetchBlock:
|
||||
Enabled: false
|
||||
|
||||
Style/RescueStandardError:
|
||||
EnforcedStyle: implicit
|
||||
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
|
@ -1 +1 @@
|
|||
3.3.2
|
||||
3.3.3
|
||||
|
|
|
@ -12,7 +12,7 @@ ARG BUILDPLATFORM=${BUILDPLATFORM}
|
|||
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
||||
# renovate: datasource=docker depName=docker.io/ruby
|
||||
ARG RUBY_VERSION="3.3.2"
|
||||
ARG RUBY_VERSION="3.3.3"
|
||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||
# renovate: datasource=node-version depName=node
|
||||
ARG NODE_MAJOR_VERSION="20"
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -171,6 +171,7 @@ group :development do
|
|||
gem 'rubocop-performance', require: false
|
||||
gem 'rubocop-rails', require: false
|
||||
gem 'rubocop-rspec', require: false
|
||||
gem 'rubocop-rspec_rails', require: false
|
||||
|
||||
# Annotates modules with schema
|
||||
gem 'annotate', '~> 3.2'
|
||||
|
|
25
Gemfile.lock
25
Gemfile.lock
|
@ -109,7 +109,7 @@ GEM
|
|||
aws-sdk-kms (1.83.0)
|
||||
aws-sdk-core (~> 3, >= 3.197.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.152.1)
|
||||
aws-sdk-s3 (1.152.2)
|
||||
aws-sdk-core (~> 3, >= 3.197.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
|
@ -584,7 +584,7 @@ GEM
|
|||
orm_adapter (0.5.0)
|
||||
ox (2.14.18)
|
||||
parallel (1.25.1)
|
||||
parser (3.3.2.0)
|
||||
parser (3.3.3.0)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
parslet (2.0.0)
|
||||
|
@ -698,8 +698,8 @@ GEM
|
|||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rexml (3.3.0)
|
||||
strscan
|
||||
rotp (6.3.0)
|
||||
rouge (4.2.1)
|
||||
rpam2 (4.0.2)
|
||||
|
@ -746,8 +746,6 @@ GEM
|
|||
parser (>= 3.3.1.0)
|
||||
rubocop-capybara (2.21.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-factory_bot (2.25.1)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-performance (1.21.0)
|
||||
rubocop (>= 1.48.1, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
|
@ -756,13 +754,11 @@ GEM
|
|||
rack (>= 1.1)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rspec (2.31.0)
|
||||
rubocop (~> 1.40)
|
||||
rubocop-capybara (~> 2.17)
|
||||
rubocop-factory_bot (~> 2.22)
|
||||
rubocop-rspec_rails (~> 2.28)
|
||||
rubocop-rspec_rails (2.28.3)
|
||||
rubocop (~> 1.40)
|
||||
rubocop-rspec (3.0.1)
|
||||
rubocop (~> 1.61)
|
||||
rubocop-rspec_rails (2.30.0)
|
||||
rubocop (~> 1.61)
|
||||
rubocop-rspec (~> 3, >= 3.0.1)
|
||||
ruby-prof (1.7.0)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-saml (1.16.0)
|
||||
|
@ -776,7 +772,7 @@ GEM
|
|||
fugit (~> 1.1, >= 1.1.6)
|
||||
safety_net_attestation (0.4.0)
|
||||
jwt (~> 2.0)
|
||||
sanitize (6.1.0)
|
||||
sanitize (6.1.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
scenic (1.8.0)
|
||||
|
@ -1028,6 +1024,7 @@ DEPENDENCIES
|
|||
rubocop-performance
|
||||
rubocop-rails
|
||||
rubocop-rspec
|
||||
rubocop-rspec_rails
|
||||
ruby-prof
|
||||
ruby-progressbar (~> 1.13)
|
||||
ruby-vips (~> 2.2)
|
||||
|
|
|
@ -78,7 +78,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
|||
### Tech stack
|
||||
|
||||
- **Ruby on Rails** powers the REST API and other web pages
|
||||
- **React.js** and Redux are used for the dynamic parts of the interface
|
||||
- **React.js** and **Redux** are used for the dynamic parts of the interface
|
||||
- **Node.js** powers the streaming API
|
||||
|
||||
### Requirements
|
||||
|
|
|
@ -68,13 +68,17 @@ export function importFetchedStatuses(statuses) {
|
|||
status.filtered.forEach(result => pushUnique(filters, result.filter));
|
||||
}
|
||||
|
||||
if (status.reblog && status.reblog.id) {
|
||||
if (status.reblog?.id) {
|
||||
processStatus(status.reblog);
|
||||
}
|
||||
|
||||
if (status.poll && status.poll.id) {
|
||||
if (status.poll?.id) {
|
||||
pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id])));
|
||||
}
|
||||
|
||||
if (status.card?.author_account) {
|
||||
pushUnique(accounts, status.card.author_account);
|
||||
}
|
||||
}
|
||||
|
||||
statuses.forEach(processStatus);
|
||||
|
|
|
@ -36,6 +36,10 @@ export function normalizeStatus(status, normalOldStatus) {
|
|||
normalStatus.poll = status.poll.id;
|
||||
}
|
||||
|
||||
if (status.card?.author_account) {
|
||||
normalStatus.card = { ...status.card, author_account: status.card.author_account.id };
|
||||
}
|
||||
|
||||
if (status.filtered) {
|
||||
normalStatus.filtered = status.filtered.map(normalizeFilterResult);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import api, { getLinks } from '../api';
|
||||
|
||||
import { importFetchedStatuses } from './importer';
|
||||
import { importFetchedStatuses, importFetchedAccounts } from './importer';
|
||||
|
||||
export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';
|
||||
export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS';
|
||||
|
@ -49,8 +49,11 @@ export const fetchTrendingLinks = () => (dispatch) => {
|
|||
dispatch(fetchTrendingLinksRequest());
|
||||
|
||||
api()
|
||||
.get('/api/v1/trends/links')
|
||||
.then(({ data }) => dispatch(fetchTrendingLinksSuccess(data)))
|
||||
.get('/api/v1/trends/links', { params: { limit: 20 } })
|
||||
.then(({ data }) => {
|
||||
dispatch(importFetchedAccounts(data.map(link => link.author_account).filter(account => !!account)));
|
||||
dispatch(fetchTrendingLinksSuccess(data));
|
||||
})
|
||||
.catch(err => dispatch(fetchTrendingLinksFail(err)));
|
||||
};
|
||||
|
||||
|
|
19
app/javascript/mastodon/components/more_from_author.jsx
Normal file
19
app/javascript/mastodon/components/more_from_author.jsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { AuthorLink } from 'mastodon/features/explore/components/author_link';
|
||||
|
||||
export const MoreFromAuthor = ({ accountId }) => (
|
||||
<div className='more-from-author'>
|
||||
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
|
||||
<use xlinkHref='#logo-symbol-icon' />
|
||||
</svg>
|
||||
|
||||
<FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <AuthorLink accountId={accountId} /> }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
MoreFromAuthor.propTypes = {
|
||||
accountId: PropTypes.string.isRequired,
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
export const AuthorLink = ({ accountId }) => {
|
||||
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
|
||||
|
||||
return (
|
||||
<Link to={`/@${account.get('acct')}`} className='story__details__shared__author-link'>
|
||||
<Avatar account={account} size={16} />
|
||||
<bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
AuthorLink.propTypes = {
|
||||
accountId: PropTypes.string.isRequired,
|
||||
};
|
|
@ -1,61 +1,89 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
|
||||
export default class Story extends PureComponent {
|
||||
import { AuthorLink } from './author_link';
|
||||
|
||||
static propTypes = {
|
||||
url: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
lang: PropTypes.string,
|
||||
publisher: PropTypes.string,
|
||||
publishedAt: PropTypes.string,
|
||||
author: PropTypes.string,
|
||||
sharedTimes: PropTypes.number,
|
||||
thumbnail: PropTypes.string,
|
||||
thumbnailDescription: PropTypes.string,
|
||||
blurhash: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
};
|
||||
const sharesCountRenderer = (displayNumber, pluralReady) => (
|
||||
<FormattedMessage
|
||||
id='link_preview.shares'
|
||||
defaultMessage='{count, plural, one {{counter} post} other {{counter} posts}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: <strong>{displayNumber}</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
state = {
|
||||
thumbnailLoaded: false,
|
||||
};
|
||||
export const Story = ({
|
||||
url,
|
||||
title,
|
||||
lang,
|
||||
publisher,
|
||||
publishedAt,
|
||||
author,
|
||||
authorAccount,
|
||||
sharedTimes,
|
||||
thumbnail,
|
||||
thumbnailDescription,
|
||||
blurhash,
|
||||
expanded
|
||||
}) => {
|
||||
const [thumbnailLoaded, setThumbnailLoaded] = useState(false);
|
||||
|
||||
handleImageLoad = () => this.setState({ thumbnailLoaded: true });
|
||||
const handleImageLoad = useCallback(() => {
|
||||
setThumbnailLoaded(true);
|
||||
}, [setThumbnailLoaded]);
|
||||
|
||||
render () {
|
||||
const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, thumbnailDescription, blurhash } = this.props;
|
||||
|
||||
const { thumbnailLoaded } = this.state;
|
||||
|
||||
return (
|
||||
<a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'>
|
||||
<div className='story__details'>
|
||||
<div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div>
|
||||
<div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div>
|
||||
<div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
|
||||
return (
|
||||
<div className={classNames('story', { expanded })}>
|
||||
<div className='story__details'>
|
||||
<div className='story__details__publisher'>
|
||||
{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}
|
||||
</div>
|
||||
|
||||
<div className='story__thumbnail'>
|
||||
{thumbnail ? (
|
||||
<>
|
||||
<div className={classNames('story__thumbnail__preview', { 'story__thumbnail__preview--hidden': thumbnailLoaded })}><Blurhash hash={blurhash} /></div>
|
||||
<img src={thumbnail} onLoad={this.handleImageLoad} alt={thumbnailDescription} title={thumbnailDescription} lang={lang} />
|
||||
</>
|
||||
) : <Skeleton />}
|
||||
<a className='story__details__title' lang={lang} href={url} target='blank' rel='noopener'>
|
||||
{title ? title : <Skeleton />}
|
||||
</a>
|
||||
|
||||
<div className='story__details__shared'>
|
||||
{author ? <FormattedMessage id='link_preview.author' className='story__details__shared__author' defaultMessage='By {name}' values={{ name: authorAccount ? <AuthorLink accountId={authorAccount} /> : <strong>{author}</strong> }} /> : <span />}
|
||||
{typeof sharedTimes === 'number' ? <span className='story__details__shared__pill'><ShortNumber value={sharedTimes} renderer={sharesCountRenderer} /></span> : <Skeleton width='10ch' />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a className='story__thumbnail' href={url} target='blank' rel='noopener'>
|
||||
{thumbnail ? (
|
||||
<>
|
||||
<div className={classNames('story__thumbnail__preview', { 'story__thumbnail__preview--hidden': thumbnailLoaded })}><Blurhash hash={blurhash} /></div>
|
||||
<img src={thumbnail} onLoad={handleImageLoad} alt={thumbnailDescription} title={thumbnailDescription} lang={lang} />
|
||||
</>
|
||||
) : <Skeleton />}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
Story.propTypes = {
|
||||
url: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
lang: PropTypes.string,
|
||||
publisher: PropTypes.string,
|
||||
publishedAt: PropTypes.string,
|
||||
author: PropTypes.string,
|
||||
authorAccount: PropTypes.string,
|
||||
sharedTimes: PropTypes.number,
|
||||
thumbnail: PropTypes.string,
|
||||
thumbnailDescription: PropTypes.string,
|
||||
blurhash: PropTypes.string,
|
||||
expanded: PropTypes.bool,
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
import Story from './components/story';
|
||||
import { Story } from './components/story';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
links: state.getIn(['trends', 'links', 'items']),
|
||||
|
@ -75,6 +75,7 @@ class Links extends PureComponent {
|
|||
publisher={link.get('provider_name')}
|
||||
publishedAt={link.get('published_at')}
|
||||
author={link.get('author_name')}
|
||||
authorAccount={link.getIn(['author_account', 'id'])}
|
||||
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
||||
thumbnail={link.get('image')}
|
||||
thumbnailDescription={link.get('image_description')}
|
||||
|
|
|
@ -6,7 +6,6 @@ import { PureComponent } from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
import Immutable from 'immutable';
|
||||
|
@ -15,9 +14,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react';
|
||||
import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react';
|
||||
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { MoreFromAuthor } from 'mastodon/components/more_from_author';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import { useBlurhash } from 'mastodon/initial_state';
|
||||
|
||||
|
@ -59,20 +58,6 @@ const addAutoPlay = html => {
|
|||
return html;
|
||||
};
|
||||
|
||||
const MoreFromAuthor = ({ author }) => (
|
||||
<div className='more-from-author'>
|
||||
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
|
||||
<use xlinkHref='#logo-symbol-icon' />
|
||||
</svg>
|
||||
|
||||
<FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <Link to={`/@${author.get('acct')}`}><Avatar account={author} size={16} /> {author.get('display_name')}</Link> }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
MoreFromAuthor.propTypes = {
|
||||
author: ImmutablePropTypes.map,
|
||||
};
|
||||
|
||||
export default class Card extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -259,7 +244,7 @@ export default class Card extends PureComponent {
|
|||
{description}
|
||||
</a>
|
||||
|
||||
{showAuthor && <MoreFromAuthor author={card.get('author_account')} />}
|
||||
{showAuthor && <MoreFromAuthor accountId={card.get('author_account')} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -308,6 +308,8 @@
|
|||
"follow_requests.unlocked_explanation": "Ваш акаўнт не схаваны, аднак прадстаўнікі {domain} палічылі, што вы можаце захацець праглядзець запыты на падпіску з гэтых профіляў уручную.",
|
||||
"follow_suggestions.curated_suggestion": "Выбар адміністрацыі",
|
||||
"follow_suggestions.dismiss": "Не паказваць зноў",
|
||||
"follow_suggestions.featured_longer": "Адабраныя камандай {domain} уручную",
|
||||
"follow_suggestions.friends_of_friends_longer": "Папулярнае сярод людзей, на якіх Вы падпісаны",
|
||||
"follow_suggestions.hints.featured": "Гэты профіль быў выбраны ўручную камандай {domain}.",
|
||||
"follow_suggestions.hints.friends_of_friends": "Гэты профіль папулярны сярод людзей, на якіх вы падпісаліся.",
|
||||
"follow_suggestions.hints.most_followed": "Гэты профіль - адзін з профіляў з самай вялікай колькасцю падпісак на {domain}.",
|
||||
|
@ -315,6 +317,8 @@
|
|||
"follow_suggestions.hints.similar_to_recently_followed": "Гэты профіль падобны на профілі, на якія вы нядаўна падпісаліся.",
|
||||
"follow_suggestions.personalized_suggestion": "Персаналізаваная прапанова",
|
||||
"follow_suggestions.popular_suggestion": "Папулярная прапанова",
|
||||
"follow_suggestions.popular_suggestion_longer": "Папулярнае на {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Падобныя профілі, за якімі вы нядаўна сачылі",
|
||||
"follow_suggestions.view_all": "Праглядзець усё",
|
||||
"follow_suggestions.who_to_follow": "На каго падпісацца",
|
||||
"followed_tags": "Падпіскі",
|
||||
|
@ -410,6 +414,7 @@
|
|||
"limited_account_hint.action": "Усе роўна паказваць профіль",
|
||||
"limited_account_hint.title": "Гэты профіль быў схаваны мадэратарамі",
|
||||
"link_preview.author": "Ад {name}",
|
||||
"link_preview.more_from_author": "Больш ад {name}",
|
||||
"lists.account.add": "Дадаць да спісу",
|
||||
"lists.account.remove": "Выдаліць са спісу",
|
||||
"lists.delete": "Выдаліць спіс",
|
||||
|
@ -458,7 +463,7 @@
|
|||
"navigation_bar.opened_in_classic_interface": "Допісы, уліковыя запісы і іншыя спецыфічныя старонкі па змоўчанні адчыняюцца ў класічным вэб-інтэрфейсе.",
|
||||
"navigation_bar.personal": "Асабістае",
|
||||
"navigation_bar.pins": "Замацаваныя допісы",
|
||||
"navigation_bar.preferences": "Параметры",
|
||||
"navigation_bar.preferences": "Налады",
|
||||
"navigation_bar.public_timeline": "Глабальная стужка",
|
||||
"navigation_bar.search": "Пошук",
|
||||
"navigation_bar.security": "Бяспека",
|
||||
|
@ -470,10 +475,22 @@
|
|||
"notification.follow_request": "{name} адправіў запыт на падпіску",
|
||||
"notification.mention": "{name} згадаў вас",
|
||||
"notification.moderation-warning.learn_more": "Даведацца больш",
|
||||
"notification.moderation_warning": "Вы атрымалі папярэджанне аб мадэрацыі",
|
||||
"notification.moderation_warning.action_delete_statuses": "Некаторыя вашыя допісы былі выдаленыя.",
|
||||
"notification.moderation_warning.action_disable": "Ваш уліковы запіс быў адключаны.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Некаторыя з вашых допісаў былі пазначаныя як далікатныя.",
|
||||
"notification.moderation_warning.action_none": "Ваш уліковы запіс атрымаў папярэджанне ад мадэратараў.",
|
||||
"notification.moderation_warning.action_sensitive": "З гэтага моманту вашыя допісы будуць пазначаныя як далікатныя.",
|
||||
"notification.moderation_warning.action_silence": "Ваш уліковы запіс быў абмежаваны.",
|
||||
"notification.moderation_warning.action_suspend": "Ваш уліковы запіс быў прыпынены.",
|
||||
"notification.own_poll": "Ваша апытанне скончылася",
|
||||
"notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
|
||||
"notification.reblog": "{name} пашырыў ваш допіс",
|
||||
"notification.relationships_severance_event": "Страціў сувязь з {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "Адміністратар з {from} прыпыніў працу {target}, што азначае, што вы больш не можаце атрымліваць ад іх абнаўлення ці ўзаемадзейнічаць з імі.",
|
||||
"notification.relationships_severance_event.domain_block": "Адміністратар з {from} заблакіраваў {target}, у тым ліку {followersCount} вашых падпісчыка(-аў) і {followingCount, plural, one {# уліковы запіс} few {# уліковыя запісы} many {# уліковых запісаў} other {# уліковых запісаў}}.",
|
||||
"notification.relationships_severance_event.learn_more": "Даведацца больш",
|
||||
"notification.relationships_severance_event.user_domain_block": "Вы заблакіравалі {target} выдаліўшы {followersCount} сваіх падпісчыкаў і {followingCount, plural, one {# уліковы запіс} few {# уліковыя запісы} many {# уліковых запісаў} other {# уліковых запісаў}}, за якімі вы сочыце.",
|
||||
"notification.status": "Новы допіс ад {name}",
|
||||
"notification.update": "Допіс {name} адрэдагаваны",
|
||||
"notification_requests.accept": "Прыняць",
|
||||
|
|
|
@ -415,6 +415,7 @@
|
|||
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
||||
"link_preview.author": "By {name}",
|
||||
"link_preview.more_from_author": "More from {name}",
|
||||
"link_preview.shares": "{count, plural, one {{counter} post} other {{counter} posts}}",
|
||||
"lists.account.add": "Add to list",
|
||||
"lists.account.remove": "Remove from list",
|
||||
"lists.delete": "Delete list",
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
"account.featured_tags.last_status_never": "کوئی مراسلہ نہیں",
|
||||
"account.featured_tags.title": "{name} کے نمایاں ہیش ٹیگز",
|
||||
"account.follow": "پیروی کریں",
|
||||
"account.follow_back": "اکاؤنٹ کو فالو بیک ",
|
||||
"account.followers": "پیروکار",
|
||||
"account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
|
||||
"account.followers.empty": "ہنوز اس صارف کی کوئی پیروی نہیں کرتا.",
|
||||
"account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}",
|
||||
"account.following": "فالو کر رہے ہیں",
|
||||
"account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
|
||||
|
@ -46,6 +47,7 @@
|
|||
"account.mute_notifications_short": "نوٹیفیکیشنز کو خاموش کریں",
|
||||
"account.mute_short": "خاموش",
|
||||
"account.muted": "خاموش کردہ",
|
||||
"account.mutual": "میوچول اکاؤنٹ",
|
||||
"account.no_bio": "کوئی تفصیل نہیں دی گئی۔",
|
||||
"account.open_original_page": "اصل صفحہ کھولیں",
|
||||
"account.posts": "ٹوٹ",
|
||||
|
@ -64,7 +66,8 @@
|
|||
"account.unmute": "@{name} کو با آواز کریں",
|
||||
"account.unmute_notifications_short": "نوٹیفیکیشنز کو خاموش نہ کریں",
|
||||
"account.unmute_short": "کو خاموش نہ کریں",
|
||||
"account_note.placeholder": "Click to add a note",
|
||||
"admin.dashboard.daily_retention": "ایڈمن ڈیش بورڈ کو ڈیلی چیک ان کریں",
|
||||
"admin.dashboard.monthly_retention": "ایڈمن کیش بورڈ کو منتھلی چیک ان کریں",
|
||||
"admin.dashboard.retention.average": "اوسط",
|
||||
"admin.dashboard.retention.cohort_size": "نئے یسرز",
|
||||
"alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
|
||||
|
|
|
@ -1411,10 +1411,15 @@ body > [data-popper-placement] {
|
|||
.audio-player,
|
||||
.attachment-list,
|
||||
.picture-in-picture-placeholder,
|
||||
.more-from-author,
|
||||
.status-card,
|
||||
.hashtag-bar {
|
||||
margin-inline-start: $thread-margin;
|
||||
width: calc(100% - ($thread-margin));
|
||||
width: calc(100% - $thread-margin);
|
||||
}
|
||||
|
||||
.more-from-author {
|
||||
width: calc(100% - $thread-margin + 2px);
|
||||
}
|
||||
|
||||
.status__content__read-more-button {
|
||||
|
@ -4129,6 +4134,13 @@ a.status-card {
|
|||
border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
.status-card.bottomless .status-card__image,
|
||||
.status-card.bottomless .status-card__image-image,
|
||||
.status-card.bottomless .status-card__image-preview {
|
||||
border-end-end-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
.status-card.expanded > a {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -8784,43 +8796,80 @@ noscript {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
padding: 15px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--background-border-color);
|
||||
gap: 15px;
|
||||
gap: 16px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $highlight-text-color;
|
||||
|
||||
.story__details__publisher,
|
||||
.story__details__shared {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__details {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&__publisher {
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: block;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
text-decoration: none;
|
||||
color: $primary-text-color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__shared {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: $darker-text-color;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
|
||||
& > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
&__pill {
|
||||
background: var(--surface-variant-background-color);
|
||||
border-radius: 4px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
padding: 4px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
&__author-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
|
@ -9891,14 +9940,14 @@ noscript {
|
|||
color: inherit;
|
||||
text-decoration: none;
|
||||
padding: 4px 12px;
|
||||
background: $ui-base-color;
|
||||
background: var(--surface-variant-background-color);
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
background: var(--surface-variant-active-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10229,6 +10278,7 @@ noscript {
|
|||
}
|
||||
|
||||
.more-from-author {
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
background: var(--surface-background-color);
|
||||
|
|
|
@ -106,4 +106,6 @@ $font-monospace: 'mastodon-font-monospace' !default;
|
|||
--background-color: #{darken($ui-base-color, 8%)};
|
||||
--background-color-tint: #{rgba(darken($ui-base-color, 8%), 0.9)};
|
||||
--surface-background-color: #{darken($ui-base-color, 4%)};
|
||||
--surface-variant-background-color: #{$ui-base-color};
|
||||
--surface-variant-active-background-color: #{lighten($ui-base-color, 4%)};
|
||||
}
|
||||
|
|
|
@ -470,10 +470,7 @@ class FeedManager
|
|||
check_for_blocks = status.active_mentions.pluck(:account_id)
|
||||
check_for_blocks.push(status.in_reply_to_account) if status.reply? && !status.in_reply_to_account_id.nil?
|
||||
|
||||
should_filter = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted)
|
||||
should_filter ||= status.account.silenced? && !Follow.exists?(account_id: receiver_id, target_account_id: status.account_id) # Filter if the account is silenced and I'm not following them
|
||||
|
||||
should_filter
|
||||
blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted)
|
||||
end
|
||||
|
||||
# Check if status should not be added to the linear direct message feed
|
||||
|
|
|
@ -152,6 +152,7 @@ class Notification < ApplicationRecord
|
|||
.limit(1),
|
||||
query
|
||||
.joins('CROSS JOIN grouped_notifications')
|
||||
.where('array_length(grouped_notifications.groups, 1) < :limit', limit: limit)
|
||||
.where('notifications.id < grouped_notifications.id')
|
||||
.where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
|
||||
.select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
|
||||
|
@ -179,6 +180,7 @@ class Notification < ApplicationRecord
|
|||
.limit(1),
|
||||
query
|
||||
.joins('CROSS JOIN grouped_notifications')
|
||||
.where('array_length(grouped_notifications.groups, 1) < :limit', limit: limit)
|
||||
.where('notifications.id > grouped_notifications.id')
|
||||
.where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
|
||||
.select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
|
||||
|
|
|
@ -45,6 +45,6 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def paginated?
|
||||
instance_options[:group_metadata].present?
|
||||
!instance_options[:group_metadata].nil?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,8 @@ class BackupService < BaseService
|
|||
|
||||
def build_outbox_json!(file)
|
||||
skeleton = serialize(collection_presenter, ActivityPub::CollectionSerializer)
|
||||
skeleton[:@context] = full_context
|
||||
skeleton[:orderedItems] = ['!PLACEHOLDER!']
|
||||
skeleton['@context'] = full_context
|
||||
skeleton['orderedItems'] = ['!PLACEHOLDER!']
|
||||
skeleton = Oj.dump(skeleton)
|
||||
prepend, append = skeleton.split('"!PLACEHOLDER!"')
|
||||
add_comma = false
|
||||
|
|
27
bin/rubocop
Executable file
27
bin/rubocop
Executable file
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rubocop' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rubocop", "rubocop")
|
|
@ -1 +1,27 @@
|
|||
---
|
||||
ur:
|
||||
activerecord:
|
||||
attributes:
|
||||
poll:
|
||||
expires_at: ڈیڈ لائن
|
||||
options: اپنی مرضی
|
||||
user:
|
||||
agreement: سروس کا معاہدہ
|
||||
email: ای میل ایڈریسز
|
||||
locale: لوکل
|
||||
password: پاس ورڈ
|
||||
user/account:
|
||||
username: یوزر نیم
|
||||
user/invite_request:
|
||||
text: وجہ
|
||||
errors:
|
||||
models:
|
||||
account:
|
||||
attributes:
|
||||
username:
|
||||
invalid: صرف حروف، نمبر اور انڈر سکور پر مشتمل ہونا چاہیے۔
|
||||
reserved: محفوظ ہے
|
||||
admin/webhook:
|
||||
attributes:
|
||||
url:
|
||||
invalid: ایک درست URL نہیں ہے۔
|
||||
|
|
|
@ -291,6 +291,7 @@ be:
|
|||
update_custom_emoji_html: "%{name} абнавіў эмодзі %{target}"
|
||||
update_domain_block_html: "%{name} абнавіў блакіроўку дамена для %{target}"
|
||||
update_ip_block_html: "%{name} змяніў правіла для IP %{target}"
|
||||
update_report_html: "%{name} абнавіў скаргу %{target}"
|
||||
update_status_html: "%{name} абнавіў допіс %{target}"
|
||||
update_user_role_html: "%{name} змяніў ролю %{target}"
|
||||
deleted_account: выдалены ўліковы запіс
|
||||
|
@ -779,6 +780,7 @@ be:
|
|||
desc_html: Гэта функцыянальнасць залежыць ад знешніх скрыптоў hCaptcha, што можа быць праблемай бяспекі і прыватнасці. Акрамя таго, <strong>гэта можа зрабіць працэс рэгістрацыі значна менш даступным для некаторых людзей, асабліва інвалідаў</strong>. Па гэтых прычынах, калі ласка, разгледзьце альтэрнатыўныя меры, такія як рэгістрацыя на аснове зацвярджэння або запрашэння.
|
||||
title: Патрабаваць ад новых карыстальнікаў рашэння CAPTCHA для пацверджання іх уліковага запісу
|
||||
content_retention:
|
||||
danger_zone: Небяспечная зона
|
||||
preamble: Кантралюйце, як створаны карыстальнікамі кантэнт захоўваецца ў Mastodon.
|
||||
title: Утрыманне кантэнту
|
||||
default_noindex:
|
||||
|
@ -983,6 +985,7 @@ be:
|
|||
delete: Выдаліць
|
||||
edit_preset: Рэдагаваць шаблон папярэджання
|
||||
empty: Вы яшчэ не вызначылі ніякіх шаблонаў папярэджанняў.
|
||||
title: Папярэджальныя прадусталёўкі
|
||||
webhooks:
|
||||
add_new: Дадаць канцавую кропку
|
||||
delete: Выдаліць
|
||||
|
|
|
@ -135,6 +135,7 @@ be:
|
|||
media: Мультымедыйныя далучэнні
|
||||
mutes: Ігнараваныя
|
||||
notifications: Апавяшчэнні
|
||||
profile: Ваш профіль Mastodon
|
||||
push: Push-апавяшчэнні
|
||||
reports: Скаргі
|
||||
search: Пошук
|
||||
|
@ -165,6 +166,7 @@ be:
|
|||
admin:write:reports: мадэраваць скаргі
|
||||
crypto: выкарыстоўваць скразное шыфраванне (end-to-end)
|
||||
follow: змяняць зносіны ўліковага запісу
|
||||
profile: чытаць толькі інфармацыю профілю вашага ўліковага запісу
|
||||
push: атрымліваць push-апавяшчэнні
|
||||
read: чытаць усе даныя вашага ўліковага запісу
|
||||
read:accounts: бачыць інфармацыю аб уліковых запісах
|
||||
|
|
|
@ -135,6 +135,7 @@ cs:
|
|||
media: Mediální přílohy
|
||||
mutes: Skrytí
|
||||
notifications: Oznámení
|
||||
profile: Váš Mastodon profil
|
||||
push: Push oznámení
|
||||
reports: Hlášení
|
||||
search: Hledání
|
||||
|
|
|
@ -135,6 +135,7 @@ fy:
|
|||
media: Mediabylagen
|
||||
mutes: Negearre
|
||||
notifications: Meldingen
|
||||
profile: Jo Mastodon-profyl
|
||||
push: Pushmeldingen
|
||||
reports: Rapportaazjes
|
||||
search: Sykje
|
||||
|
@ -165,6 +166,7 @@ fy:
|
|||
admin:write:reports: moderaasjemaatregelen nimme yn rapportaazjes
|
||||
crypto: ein-ta-ein-fersifering brûke
|
||||
follow: relaasjes tusken accounts bewurkje
|
||||
profile: allinnich de profylgegevens fan jo account lêze
|
||||
push: jo pushmeldingen ûntfange
|
||||
read: alle gegevens fan jo account lêze
|
||||
read:accounts: accountynformaasje besjen
|
||||
|
|
|
@ -135,6 +135,7 @@ ia:
|
|||
media: Annexos multimedial
|
||||
mutes: Silentiates
|
||||
notifications: Notificationes
|
||||
profile: Tu profilo de Mastodon
|
||||
push: Notificationes push
|
||||
reports: Reportos
|
||||
search: Cercar
|
||||
|
@ -165,6 +166,7 @@ ia:
|
|||
admin:write:reports: exequer actiones de moderation sur reportos
|
||||
crypto: usar cryptation de puncta a puncta
|
||||
follow: modificar relationes inter contos
|
||||
profile: leger solmente le information de profilo de tu conto
|
||||
push: reciper tu notificationes push
|
||||
read: leger tote le datos de tu conto
|
||||
read:accounts: vider informationes de contos
|
||||
|
|
|
@ -135,6 +135,7 @@ sl:
|
|||
media: Predstavnostne priloge
|
||||
mutes: Utišani
|
||||
notifications: Obvestila
|
||||
profile: Vaš profil Mastodon
|
||||
push: Potisna obvestila
|
||||
reports: Prijave
|
||||
search: Iskanje
|
||||
|
@ -165,6 +166,7 @@ sl:
|
|||
admin:write:reports: izvedi moderirana dejanja na prijavah
|
||||
crypto: Uporabi šifriranje od konca do konca
|
||||
follow: spremeni razmerja med računi
|
||||
profile: preberi le podatke profila računa
|
||||
push: prejmi potisna obvestila
|
||||
read: preberi vse podatke svojega računa
|
||||
read:accounts: oglejte si podrobnosti računov
|
||||
|
|
|
@ -166,6 +166,7 @@ sv:
|
|||
admin:write:reports: utföra modereringsåtgärder på rapporter
|
||||
crypto: använd obruten kryptering
|
||||
follow: modifiera kontorelationer
|
||||
profile: läs endast ditt kontos profilinformation
|
||||
push: ta emot dina push-notiser
|
||||
read: läsa dina kontodata
|
||||
read:accounts: se kontoinformation
|
||||
|
|
|
@ -285,6 +285,7 @@ fy:
|
|||
update_custom_emoji_html: Emoji %{target} is troch %{name} bywurke
|
||||
update_domain_block_html: "%{name} hat de domeinblokkade bywurke foar %{target}"
|
||||
update_ip_block_html: "%{name} hat de rigel foar IP %{target} wizige"
|
||||
update_report_html: Rapportaazje %{target} is troch %{name} bywurke
|
||||
update_status_html: "%{name} hat de berjochten %{target} bywurke"
|
||||
update_user_role_html: "%{name} hat de rol %{target} wizige"
|
||||
deleted_account: fuortsmiten account
|
||||
|
|
|
@ -77,10 +77,15 @@ be:
|
|||
warn: Схаваць адфільтраваны кантэнт за папярэджаннем з назвай фільтру
|
||||
form_admin_settings:
|
||||
activity_api_enabled: Падлік лакальна апублікаваных пастоў, актыўных карыстальнікаў і новых рэгістрацый у тыдзень
|
||||
app_icon: WEBP, PNG, GIF ці JPG. Заменіце прадвызначаны значок праграмы на мабільных прыладах карыстальніцкім значком.
|
||||
backups_retention_period: Карыстальнікі могуць ствараць архівы сваіх допісаў для наступнай запампоўкі. Пры станоўчай колькасці дзён гэтыя архівы будуць аўтаматычна выдаляцца са сховішча пасля заканчэння названай колькасці дзён.
|
||||
bootstrap_timeline_accounts: Гэтыя ўліковыя запісы будуць замацаваны ў топе рэкамендацый для новых карыстальнікаў.
|
||||
closed_registrations_message: Паказваецца, калі рэгістрацыя закрытая
|
||||
content_cache_retention_period: Усе допісы з іншых сервераў (уключаючы пашырэнні і адказы) будуць выдаленыя праз паказаную колькасць дзён, незалежна ад таго, як лакальны карыстальнік узаемадзейнічаў з гэтымі допісамі. Гэта датычыцца і тых допісаў, якія лакальны карыстальнік пазначыў у закладкі або ўпадабанае. Прыватныя згадкі паміж карыстальнікамі з розных інстанс таксама будуць страчаныя і не змогуць быць адноўлены. Выкарыстанне гэтай налады прызначана для асобнікаў спецыяльнага прызначэння і парушае многія чаканні карыстальнікаў пры выкарыстанні ў агульных мэтах.
|
||||
custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon.
|
||||
favicon: WEBP, PNG, GIF ці JPG. Замяняе прадвызначаны favicon Mastodon на ўласны значок.
|
||||
mascot: Замяняе ілюстрацыю ў пашыраным вэб-інтэрфейсе.
|
||||
media_cache_retention_period: Медыяфайлы з допісаў, зробленых выдаленымі карыстальнікамі, кэшыруюцца на вашым серверы. Пры станоўчым значэнні медыяфайлы будуць выдалены праз пазначаную колькасць дзён. Калі медыядадзеныя будуць запытаны пасля выдалення, яны будуць загружаны паўторна, калі зыходны кантэнт усё яшчэ даступны. У сувязі з абмежаваннямі на частату абнаўлення відарысаў іншых сайтаў, рэкамендуецца ўсталяваць значэнне не менш за 14 дзён, інакш відарысы не будуць загружацца па запыце раней за гэты тэрмін.
|
||||
peers_api_enabled: Спіс даменных імён, з якімі сутыкнуўся гэты сервер у федэсвеце. Дадзеныя аб тым, ці знаходзіцеся вы з пэўным серверам у федэрацыі, не ўключаныя, ёсць толькі тое, што ваш сервер ведае пра гэта. Гэта выкарыстоўваецца сэрвісамі, якія збіраюць статыстыку па федэрацыі ў агульным сэнсе.
|
||||
profile_directory: Дырэкторыя профіляў змяшчае спіс усіх карыстальнікаў, якія вырашылі быць бачнымі.
|
||||
require_invite_text: Калі рэгістрацыя патрабуе ручнога пацвержання, зрабіце поле "Чаму вы хочаце далучыцца?" абавязковым
|
||||
|
@ -240,6 +245,7 @@ be:
|
|||
backups_retention_period: Працягласць захавання архіву карыстальніка
|
||||
bootstrap_timeline_accounts: Заўсёды раіць гэтыя ўліковыя запісы новым карыстальнікам
|
||||
closed_registrations_message: Уласнае паведамленне, калі рэгістрацыя немагчымая
|
||||
content_cache_retention_period: Перыяд захоўвання выдаленага змесціва
|
||||
custom_css: CSS карыстальніка
|
||||
mascot: Уласны маскот(спадчына)
|
||||
media_cache_retention_period: Працягласць захавання кэшу для медыя
|
||||
|
|
|
@ -77,11 +77,13 @@ fy:
|
|||
warn: Ferstopje de filtere ynhâld efter in warskôging, mei de titel fan it filter as warskôgingstekst
|
||||
form_admin_settings:
|
||||
activity_api_enabled: Tal lokaal publisearre artikelen, aktive brûkers en nije registraasjes yn wyklikse werjefte
|
||||
app_icon: WEBP, PNG, GIF of JPG. Ferfangt op mobile apparaten it standert app-pictogram mei in oanpast piktogram.
|
||||
backups_retention_period: Brûkers hawwe de mooglikheid om argiven fan harren berjochten te generearjen om letter te downloaden. Wannear ynsteld op in positive wearde, wurde dizze argiven automatysk fuortsmiten út jo ûnthâld nei it opjûne oantal dagen.
|
||||
bootstrap_timeline_accounts: Dizze accounts wurde boppe oan de oanrekommandaasjes oan nije brûkers toand. Meardere brûkersnammen troch komma’s skiede.
|
||||
closed_registrations_message: Werjûn wannear’t registraasje fan nije accounts útskeakele is
|
||||
content_cache_retention_period: Alle berjochten fan oare servers (ynklusyf boosts en reaksjes) wurde fuortsmiten nei it opjûne oantal dagen, nettsjinsteande iennige lokale brûkersynteraksje mei dy berjochten. Dit oanbelanget ek berjochten dy’t in lokale brûker oan harren blêdwizers tafoege hat of as favoryt markearre hat. Priveeberjochten tusken brûkers fan ferskate servers gean ek ferlern en binne ûnmooglik te werstellen. It gebrûk fan dizze ynstelling is bedoeld foar servers dy’t in spesjaal doel tsjinje en oertrêdet in protte brûkersferwachtingen wannear’t dizze foar algemien gebrûk ymplemintearre wurdt.
|
||||
custom_css: Jo kinne oanpaste CSS tapasse op de webferzje fan dizze Mastodon-server.
|
||||
favicon: WEBP, PNG, GIF of JPG. Ferfangt de standert Mastodon-favicon mei in oanpast piktogram.
|
||||
mascot: Oerskriuwt de yllustraasje yn de avansearre webomjouwing.
|
||||
media_cache_retention_period: Mediabestannen fan berjochten fan eksterne brûkers wurde op jo server yn de buffer bewarre. Wannear ynsteld op in positive wearde, wurde media fuortsmiten nei it opjûne oantal dagen. As de mediagegevens opfrege wurde neidat se fuortsmiten binne, wurde se opnij download wannear de orizjinele ynhâld noch hieltyd beskikber is. Fanwegen beheiningen op hoe faak keppelingsfoarbylden websites fan tredden rieplachtsje, wurdt oanrekommandearre om dizze wearde yn te stellen op op syn minste 14 dagen. Oars wurde keppelingsfoarbylden net op oanfraach bywurke.
|
||||
peers_api_enabled: In list mei domeinnammen, dêr’t dizze server yn fediverse kontakt hân mei hat. Hjir wurdt gjin data dield, oft jo mei in bepaalde server federearrest, mar alinnich, dat jo server dat wit. Dit wurdt foar tsjinsten brûkt, dy’t statistiken oer federaasje yn algemiene sin sammelet.
|
||||
|
|
|
@ -77,11 +77,13 @@ sv:
|
|||
warn: Dölj det filtrerade innehållet bakom en varning som visar filtrets rubrik
|
||||
form_admin_settings:
|
||||
activity_api_enabled: Antalet lokalt publicerade inlägg, aktiva användare och nya registrerade konton per vecka
|
||||
app_icon: WEBP, PNG, GIF eller JPG. Använd istället för appens egna ikon på mobila enheter.
|
||||
backups_retention_period: Användare har möjlighet att generera arkiv av sina inlägg för att ladda ned senare. När det sätts till ett positivt värde raderas dessa arkiv automatiskt från din lagring efter det angivna antalet dagar.
|
||||
bootstrap_timeline_accounts: Dessa konton kommer fästas högst upp i nya användares följrekommendationer.
|
||||
closed_registrations_message: Visas när nyregistreringar är avstängda
|
||||
content_cache_retention_period: Alla inlägg från andra servrar (inklusive booster och svar) kommer att raderas efter det angivna antalet dagar, utan hänsyn till någon lokal användarinteraktion med dessa inlägg. Detta inkluderar inlägg där en lokal användare har markerat det som bokmärke eller favoriter. Privata omnämnanden mellan användare från olika instanser kommer också att gå förlorade och blir omöjliga att återställa. Användningen av denna inställning är avsedd för specialfall och bryter många användarförväntningar när de implementeras för allmänt bruk.
|
||||
custom_css: Du kan använda anpassade stilar på webbversionen av Mastodon.
|
||||
favicon: WEBP, PNG, GIF eller JPG. Används på mobila enheter istället för appens egen ikon.
|
||||
mascot: Åsidosätter illustrationen i det avancerade webbgränssnittet.
|
||||
media_cache_retention_period: Mediafiler från inlägg som gjorts av fjärranvändare cachas på din server. När inställd på ett positivt värde kommer media att raderas efter det angivna antalet dagar. Om mediadatat begärs efter att det har raderats, kommer det att laddas ned igen om källinnehållet fortfarande är tillgängligt. På grund av begränsningar för hur ofta förhandsgranskningskort för länkar hämtas från tredjepartswebbplatser, rekommenderas det att ange detta värde till minst 14 dagar, annars kommer förhandsgranskningskorten inte att uppdateras på begäran före den tiden.
|
||||
peers_api_enabled: En lista över domänen den här servern har stött på i fediversum. Ingen data inkluderas om du har federerat med servern, bara att din server känner till den. Detta används av tjänster som samlar statistik om federering i allmänhet.
|
||||
|
|
|
@ -951,6 +951,7 @@ sv:
|
|||
delete: Radera
|
||||
edit_preset: Redigera varningsförval
|
||||
empty: Du har inte definierat några varningsförval ännu.
|
||||
title: Varning förinställningar
|
||||
webhooks:
|
||||
add_new: Lägg till slutpunkt
|
||||
delete: Ta bort
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const config = {
|
||||
'*': 'prettier --ignore-unknown --write',
|
||||
'Capfile|Gemfile|*.{rb,ruby,ru,rake}':
|
||||
'bundle exec rubocop --force-exclusion -a',
|
||||
'Capfile|Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
|
||||
'*.{js,jsx,ts,tsx}': 'eslint --fix',
|
||||
'*.{css,scss}': 'stylelint --fix',
|
||||
'*.haml': 'bundle exec haml-lint -a',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@mastodon/mastodon",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"packageManager": "yarn@4.2.2",
|
||||
"packageManager": "yarn@4.3.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
|
|
@ -213,13 +213,13 @@ RSpec.describe FeedManager do
|
|||
expect(described_class.instance.filter?(:mentions, reply, bob)).to be true
|
||||
end
|
||||
|
||||
it 'returns true for status by silenced account who recipient is not following' do
|
||||
it 'returns false for status by limited account who recipient is not following' do
|
||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||
alice.silence!
|
||||
expect(described_class.instance.filter?(:mentions, status, bob)).to be true
|
||||
expect(described_class.instance.filter?(:mentions, status, bob)).to be false
|
||||
end
|
||||
|
||||
it 'returns false for status by followed silenced account' do
|
||||
it 'returns false for status by followed limited account' do
|
||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||
alice.silence!
|
||||
bob.follow!(alice)
|
||||
|
|
|
@ -58,6 +58,7 @@ RSpec.describe 'Notifications' do
|
|||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_types.uniq).to eq ['mention']
|
||||
expect(body_as_json[0][:page_min_id]).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -55,9 +55,11 @@ RSpec.describe BackupService do
|
|||
end
|
||||
|
||||
def expect_outbox_export
|
||||
json = export_json(:outbox)
|
||||
body = export_json_raw(:outbox)
|
||||
json = Oj.load(body)
|
||||
|
||||
aggregate_failures do
|
||||
expect(body.scan('@context').count).to eq 1
|
||||
expect(json['@context']).to_not be_nil
|
||||
expect(json['type']).to eq 'OrderedCollection'
|
||||
expect(json['totalItems']).to eq 2
|
||||
|
@ -85,8 +87,12 @@ RSpec.describe BackupService do
|
|||
end
|
||||
end
|
||||
|
||||
def export_json_raw(type)
|
||||
read_zip_file(backup, "#{type}.json")
|
||||
end
|
||||
|
||||
def export_json(type)
|
||||
Oj.load(read_zip_file(backup, "#{type}.json"))
|
||||
Oj.load(export_json_raw(type))
|
||||
end
|
||||
|
||||
def include_create_item(status)
|
||||
|
|
|
@ -129,6 +129,35 @@ RSpec.describe NotifyService do
|
|||
end
|
||||
end
|
||||
|
||||
describe NotifyService::DismissCondition do
|
||||
subject { described_class.new(notification) }
|
||||
|
||||
let(:activity) { Fabricate(:mention, status: Fabricate(:status)) }
|
||||
let(:notification) { Fabricate(:notification, type: :mention, activity: activity, from_account: activity.status.account, account: activity.account) }
|
||||
|
||||
describe '#dismiss?' do
|
||||
context 'when sender is silenced' do
|
||||
before do
|
||||
notification.from_account.silence!
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.dismiss?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when recipient has blocked sender' do
|
||||
before do
|
||||
notification.account.block!(notification.from_account)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.dismiss?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe NotifyService::FilterCondition do
|
||||
subject { described_class.new(notification) }
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@mastodon/streaming",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"packageManager": "yarn@4.2.2",
|
||||
"packageManager": "yarn@4.3.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -13117,8 +13117,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"pino@npm:^9.0.0":
|
||||
version: 9.1.0
|
||||
resolution: "pino@npm:9.1.0"
|
||||
version: 9.2.0
|
||||
resolution: "pino@npm:9.2.0"
|
||||
dependencies:
|
||||
atomic-sleep: "npm:^1.0.0"
|
||||
fast-redact: "npm:^3.1.1"
|
||||
|
@ -13133,7 +13133,7 @@ __metadata:
|
|||
thread-stream: "npm:^3.0.0"
|
||||
bin:
|
||||
pino: bin.js
|
||||
checksum: 10c0/d060530ae2e4e8f21d04bb0f44f009f94d207d7f4337f508f618416514214ddaf1b29f8c5c265153a19ce3b6480b451461f40020f916ace9d53a5aa07624b79c
|
||||
checksum: 10c0/5fbd226ff7dab0961232b5aa5eca0530cdc5bb29f6bf17d929e42239293b1a587a26cc311db6abc1090c9dd57e8f7b031eae341b41d00d4a642b4f1736474c80
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -15379,15 +15379,15 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"sass@npm:^1.62.1":
|
||||
version: 1.77.4
|
||||
resolution: "sass@npm:1.77.4"
|
||||
version: 1.77.5
|
||||
resolution: "sass@npm:1.77.5"
|
||||
dependencies:
|
||||
chokidar: "npm:>=3.0.0 <4.0.0"
|
||||
immutable: "npm:^4.0.0"
|
||||
source-map-js: "npm:>=0.6.2 <2.0.0"
|
||||
bin:
|
||||
sass: sass.js
|
||||
checksum: 10c0/b9cb4882bded282aabe38d011adfce375e1f282184fcf93dc3da5d5be834c6aa53c474c15634c351ef7bd85146cfd1cc81343654cc3bcf000d78e856da4225ef
|
||||
checksum: 10c0/9da049b0a3fadab419084d6becdf471e107cf6e3c8ac87cabea2feb845afac75e86c99e06ee721a5aa4f6a2d833ec5380137c4e540ab2f760edf1e4eb6139e69
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in a new issue