Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
6f73d7eedd
295 changed files with 4425 additions and 2546 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
|
|
|
@ -1,20 +1,15 @@
|
||||||
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
||||||
FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye
|
FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
|
||||||
|
|
||||||
# Install Rails
|
# Install node version from .nvmrc
|
||||||
# RUN gem install rails webdrivers
|
WORKDIR /app
|
||||||
|
COPY .nvmrc .
|
||||||
|
RUN /bin/bash --login -i -c "nvm install"
|
||||||
|
|
||||||
ARG NODE_VERSION="20"
|
# Install additional OS packages
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
|
RUN apt-get update && \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive && \
|
||||||
|
apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
|
||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
# Move welcome message to where VS Code expects it
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
COPY .devcontainer/welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
||||||
&& apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install additional gems.
|
|
||||||
RUN gem install foreman
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install global node packages.
|
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && corepack enable" 2>&1
|
|
||||||
|
|
||||||
COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Mastodon on GitHub Codespaces",
|
"name": "Mastodon on GitHub Codespaces",
|
||||||
"dockerComposeFile": "../docker-compose.yml",
|
"dockerComposeFile": "../compose.yaml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"remoteUser": "root",
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent"
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
|
@ -37,7 +39,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
"postCreateCommand": "bin/setup",
|
||||||
"waitFor": "postCreateCommand",
|
"waitFor": "postCreateCommand",
|
||||||
|
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
working_dir: /workspaces/mastodon/
|
working_dir: /workspaces/mastodon/
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: Dockerfile
|
dockerfile: .devcontainer/Dockerfile
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/workspaces:cached
|
- ..:/workspaces/mastodon:cached
|
||||||
environment:
|
environment:
|
||||||
RAILS_ENV: development
|
RAILS_ENV: development
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Mastodon on local machine",
|
"name": "Mastodon on local machine",
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
"dockerComposeFile": "compose.yaml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
|
@ -23,12 +23,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"remoteUser": "root",
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent"
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
"postCreateCommand": "bin/setup",
|
||||||
"waitFor": "postCreateCommand",
|
"waitFor": "postCreateCommand",
|
||||||
|
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Fail the whole script on first error
|
|
||||||
|
|
||||||
# Fetch Ruby gem dependencies
|
|
||||||
bundle config path 'vendor/bundle'
|
|
||||||
bundle config with 'development test'
|
|
||||||
bundle install
|
|
||||||
|
|
||||||
# Make Gemfile.lock pristine again
|
|
||||||
git checkout -- Gemfile.lock
|
|
||||||
|
|
||||||
# Fetch Javascript dependencies
|
|
||||||
corepack prepare
|
|
||||||
yarn install --immutable
|
|
||||||
|
|
||||||
# [re]create, migrate, and seed the test database
|
|
||||||
RAILS_ENV=test ./bin/rails db:setup
|
|
||||||
|
|
||||||
# [re]create, migrate, and seed the development database
|
|
||||||
RAILS_ENV=development ./bin/rails db:setup
|
|
||||||
|
|
||||||
# Precompile assets for development
|
|
||||||
RAILS_ENV=development ./bin/rails assets:precompile
|
|
||||||
|
|
||||||
# Precompile assets for test
|
|
||||||
RAILS_ENV=test ./bin/rails assets:precompile
|
|
|
@ -1,8 +1,7 @@
|
||||||
👋 Welcome to "Mastodon" in GitHub Codespaces!
|
👋 Welcome to your Mastodon Dev Container!
|
||||||
|
|
||||||
🛠️ Your environment is fully setup with all the required software.
|
🛠️ Your environment is fully setup with all the required software.
|
||||||
|
|
||||||
🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
|
💥 Run `bin/dev` to start the application processes.
|
||||||
|
|
||||||
📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.
|
|
||||||
|
|
||||||
|
🥼 Run `RAILS_ENV=test bin/rails assets:precompile && RAILS_ENV=test bin/rspec` to run the test suite.
|
||||||
|
|
|
@ -349,6 +349,9 @@ module.exports = defineConfig({
|
||||||
// Disable formatting rules that have been enabled in the base config
|
// Disable formatting rules that have been enabled in the base config
|
||||||
'indent': 'off',
|
'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'],
|
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||||
|
|
||||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||||
|
|
2
.github/actions/setup-ruby/action.yml
vendored
2
.github/actions/setup-ruby/action.yml
vendored
|
@ -14,7 +14,7 @@ runs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev ${{ inputs.additional-system-dependencies }}
|
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||||
|
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
|
|
4
.github/codecov.yml
vendored
4
.github/codecov.yml
vendored
|
@ -3,9 +3,9 @@ coverage:
|
||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
# Github status check is not blocking
|
# GitHub status check is not blocking
|
||||||
informational: true
|
informational: true
|
||||||
patch:
|
patch:
|
||||||
default:
|
default:
|
||||||
# Github status check is not blocking
|
# GitHub status check is not blocking
|
||||||
informational: true
|
informational: true
|
||||||
|
|
3
.github/renovate.json5
vendored
3
.github/renovate.json5
vendored
|
@ -2,6 +2,7 @@
|
||||||
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||||
extends: [
|
extends: [
|
||||||
'config:recommended',
|
'config:recommended',
|
||||||
|
'customManagers:dockerfileVersions',
|
||||||
':labels(dependencies)',
|
':labels(dependencies)',
|
||||||
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
||||||
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
dependencyDashboardApproval: true,
|
dependencyDashboardApproval: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Update Github Actions and Docker images weekly
|
// Update GitHub Actions and Docker images weekly
|
||||||
matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
|
matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
|
||||||
extends: ['schedule:weekly'],
|
extends: ['schedule:weekly'],
|
||||||
},
|
},
|
||||||
|
|
2
.github/workflows/build-container-image.yml
vendored
2
.github/workflows/build-container-image.yml
vendored
|
@ -68,7 +68,7 @@ jobs:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Log in to the Github Container registry
|
- name: Log in to the GitHub Container registry
|
||||||
if: contains(inputs.push_to_images, 'ghcr.io')
|
if: contains(inputs.push_to_images, 'ghcr.io')
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
13
.github/workflows/bundler-audit.yml
vendored
13
.github/workflows/bundler-audit.yml
vendored
|
@ -6,14 +6,12 @@ on:
|
||||||
paths:
|
paths:
|
||||||
- 'Gemfile*'
|
- 'Gemfile*'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
- '.bundler-audit.yml'
|
|
||||||
- '.github/workflows/bundler-audit.yml'
|
- '.github/workflows/bundler-audit.yml'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'Gemfile*'
|
- 'Gemfile*'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
- '.bundler-audit.yml'
|
|
||||||
- '.github/workflows/bundler-audit.yml'
|
- '.github/workflows/bundler-audit.yml'
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
|
@ -23,12 +21,17 @@ jobs:
|
||||||
security:
|
security:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUNDLE_ONLY: development
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby environment
|
- name: Set up Ruby
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Run bundler-audit
|
- name: Run bundler-audit
|
||||||
run: bundle exec bundler-audit
|
run: bundle exec bundler-audit check --update
|
||||||
|
|
4
.github/workflows/crowdin-download.yml
vendored
4
.github/workflows/crowdin-download.yml
vendored
|
@ -58,13 +58,13 @@ jobs:
|
||||||
title: 'New Crowdin Translations (automated)'
|
title: 'New Crowdin Translations (automated)'
|
||||||
author: 'GitHub Actions <noreply@github.com>'
|
author: 'GitHub Actions <noreply@github.com>'
|
||||||
body: |
|
body: |
|
||||||
New Crowdin translations, automated with Github Actions
|
New Crowdin translations, automated with GitHub Actions
|
||||||
|
|
||||||
See `.github/workflows/crowdin-download.yml`
|
See `.github/workflows/crowdin-download.yml`
|
||||||
|
|
||||||
This PR will be updated every day with new translations.
|
This PR will be updated every day with new translations.
|
||||||
|
|
||||||
Due to a limitation in Github Actions, checks are not running on this PR without manual action.
|
Due to a limitation in GitHub Actions, checks are not running on this PR without manual action.
|
||||||
If you want to run the checks, then close and re-open it.
|
If you want to run the checks, then close and re-open it.
|
||||||
branch: i18n/crowdin/translations
|
branch: i18n/crowdin/translations
|
||||||
base: main
|
base: main
|
||||||
|
|
10
.github/workflows/lint-haml.yml
vendored
10
.github/workflows/lint-haml.yml
vendored
|
@ -26,12 +26,18 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUNDLE_ONLY: development
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby environment
|
- name: Set up Ruby
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Run haml-lint
|
- name: Run haml-lint
|
||||||
run: |
|
run: |
|
||||||
|
|
13
.github/workflows/lint-ruby.yml
vendored
13
.github/workflows/lint-ruby.yml
vendored
|
@ -27,19 +27,24 @@ jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUNDLE_ONLY: development
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby environment
|
- name: Set up Ruby
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Set-up RuboCop Problem Matcher
|
- name: Set-up RuboCop Problem Matcher
|
||||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||||
|
|
||||||
- name: Run rubocop
|
- name: Run rubocop
|
||||||
run: bundle exec rubocop
|
run: bin/rubocop
|
||||||
|
|
||||||
- name: Run brakeman
|
- name: Run brakeman
|
||||||
if: always() # Run both checks, even if the first failed
|
if: always() # Run both checks, even if the first failed
|
||||||
run: bundle exec brakeman
|
run: bin/brakeman
|
||||||
|
|
2
.github/workflows/rebase-needed.yml
vendored
2
.github/workflows/rebase-needed.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check for merge conflicts
|
- name: Check for merge conflicts
|
||||||
uses: eps1lon/actions-label-merge-conflict@releases/2.x
|
uses: eps1lon/actions-label-merge-conflict@v3
|
||||||
with:
|
with:
|
||||||
dirtyLabel: 'rebase needed :construction:'
|
dirtyLabel: 'rebase needed :construction:'
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
|
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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
|
@ -17,7 +18,7 @@ jobs:
|
||||||
- id: skip_check
|
- id: skip_check
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
with:
|
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:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -40,9 +41,9 @@ jobs:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
|
@ -50,14 +51,13 @@ jobs:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "redis-cli ping"
|
--health-cmd "redis-cli ping"
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CONTINUOUS_INTEGRATION: true
|
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
|
@ -65,7 +65,7 @@ jobs:
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
BUNDLE_CLEAN: true
|
BUNDLE_CLEAN: true
|
||||||
BUNDLE_FROZEN: true
|
BUNDLE_FROZEN: true
|
||||||
BUNDLE_WITHOUT: 'development production'
|
BUNDLE_WITHOUT: 'development:production'
|
||||||
BUNDLE_JOBS: 3
|
BUNDLE_JOBS: 3
|
||||||
BUNDLE_RETRY: 3
|
BUNDLE_RETRY: 3
|
||||||
|
|
||||||
|
@ -75,14 +75,19 @@ jobs:
|
||||||
- name: Set up Ruby environment
|
- name: Set up Ruby environment
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- name: Create database
|
- name: Test "one step migration" flow
|
||||||
run: './bin/rails db:create'
|
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
|
- name: Test "two step migration" flow
|
||||||
run: './bin/rails tests:migrations:prepare_database'
|
run: |
|
||||||
|
bin/rails db:drop
|
||||||
- name: Run all remaining migrations
|
bin/rails db:create
|
||||||
run: './bin/rails db:migrate'
|
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
|
||||||
|
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
|
||||||
- name: Check migration result
|
bin/rails db:migrate
|
||||||
run: './bin/rails tests:migrations:check_database'
|
bin/rails tests:migrations:check_database
|
147
.github/workflows/test-ruby.yml
vendored
147
.github/workflows/test-ruby.yml
vendored
|
@ -28,11 +28,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
RAILS_ENV: ${{ matrix.mode }}
|
RAILS_ENV: ${{ matrix.mode }}
|
||||||
BUNDLE_WITH: ${{ matrix.mode }}
|
BUNDLE_WITH: ${{ matrix.mode }}
|
||||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: precompile_placeholder
|
SECRET_KEY_BASE_DUMMY: 1
|
||||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: precompile_placeholder
|
|
||||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: precompile_placeholder
|
|
||||||
OTP_SECRET: precompile_placeholder
|
|
||||||
SECRET_KEY_BASE: precompile_placeholder
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -77,9 +73,9 @@ jobs:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
|
@ -87,9 +83,9 @@ jobs:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "redis-cli ping"
|
--health-cmd "redis-cli ping"
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
|
@ -133,7 +129,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
additional-system-dependencies: ffmpeg libpam-dev
|
||||||
|
|
||||||
- name: Load database schema
|
- name: Load database schema
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
@ -148,6 +144,93 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
test-libvips:
|
||||||
|
name: Libvips tests
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:14-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10ms
|
||||||
|
--health-timeout 3s
|
||||||
|
--health-retries 50
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
options: >-
|
||||||
|
--health-cmd "redis-cli ping"
|
||||||
|
--health-interval 10ms
|
||||||
|
--health-timeout 3s
|
||||||
|
--health-retries 50
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
env:
|
||||||
|
DB_HOST: localhost
|
||||||
|
DB_USER: postgres
|
||||||
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||||
|
RAILS_ENV: test
|
||||||
|
ALLOW_NOPAM: true
|
||||||
|
PAM_ENABLED: true
|
||||||
|
PAM_DEFAULT_SERVICE: pam_test
|
||||||
|
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||||
|
OIDC_ENABLED: true
|
||||||
|
OIDC_SCOPE: read
|
||||||
|
SAML_ENABLED: true
|
||||||
|
CAS_ENABLED: true
|
||||||
|
BUNDLE_WITH: 'pam_authentication test'
|
||||||
|
GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
|
||||||
|
MASTODON_USE_LIBVIPS: true
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
ruby-version:
|
||||||
|
- '3.1'
|
||||||
|
- '3.2'
|
||||||
|
- '.ruby-version'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: './'
|
||||||
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Expand archived asset artifacts
|
||||||
|
run: |
|
||||||
|
tar xvzf artifacts.tar.gz
|
||||||
|
|
||||||
|
- name: Set up Ruby environment
|
||||||
|
uses: ./.github/actions/setup-ruby
|
||||||
|
with:
|
||||||
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
|
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
|
||||||
|
|
||||||
|
- name: Load database schema
|
||||||
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
|
||||||
|
- run: bin/rspec --tag paperclip_processing
|
||||||
|
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
if: matrix.ruby-version == '.ruby-version'
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: coverage/lcov/mastodon.lcov
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
test-e2e:
|
test-e2e:
|
||||||
name: End to End testing
|
name: End to End testing
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -163,9 +246,9 @@ jobs:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
|
@ -173,9 +256,9 @@ jobs:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "redis-cli ping"
|
--health-cmd "redis-cli ping"
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
|
@ -209,7 +292,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick
|
additional-system-dependencies: ffmpeg
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
@ -248,9 +331,9 @@ jobs:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
|
@ -258,9 +341,9 @@ jobs:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "redis-cli ping"
|
--health-cmd "redis-cli ping"
|
||||||
--health-interval 10s
|
--health-interval 10ms
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 5
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
|
@ -271,9 +354,9 @@ jobs:
|
||||||
xpack.security.enabled: false
|
xpack.security.enabled: false
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "curl http://localhost:9200/_cluster/health"
|
--health-cmd "curl http://localhost:9200/_cluster/health"
|
||||||
--health-interval 10s
|
--health-interval 2s
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 10
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 9200:9200
|
- 9200:9200
|
||||||
|
|
||||||
|
@ -285,9 +368,9 @@ jobs:
|
||||||
DISABLE_SECURITY_PLUGIN: true
|
DISABLE_SECURITY_PLUGIN: true
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "curl http://localhost:9200/_cluster/health"
|
--health-cmd "curl http://localhost:9200/_cluster/health"
|
||||||
--health-interval 10s
|
--health-interval 2s
|
||||||
--health-timeout 5s
|
--health-timeout 3s
|
||||||
--health-retries 10
|
--health-retries 50
|
||||||
ports:
|
ports:
|
||||||
- 9200:9200
|
- 9200:9200
|
||||||
|
|
||||||
|
@ -329,7 +412,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick
|
additional-system-dependencies: ffmpeg
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
|
19
.nanoignore
19
.nanoignore
|
@ -1,19 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
.bundle/
|
|
||||||
.cache/
|
|
||||||
config/deploy/*
|
|
||||||
coverage
|
|
||||||
docs/
|
|
||||||
.env
|
|
||||||
log/*.log
|
|
||||||
neo4j/
|
|
||||||
node_modules/
|
|
||||||
public/assets/
|
|
||||||
public/system/
|
|
||||||
spec/
|
|
||||||
tmp/
|
|
||||||
.vagrant/
|
|
||||||
vendor/bundle/
|
|
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:
|
inherit_mode:
|
||||||
merge:
|
merge:
|
||||||
- Exclude
|
- Exclude
|
||||||
|
@ -12,229 +39,3 @@ require:
|
||||||
- rubocop-rspec_rails
|
- rubocop-rspec_rails
|
||||||
- rubocop-performance
|
- rubocop-performance
|
||||||
- rubocop-capybara
|
- 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
|
27
.rubocop/rspec.yml
Normal file
27
.rubocop/rspec.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
RSpec/ExampleLength:
|
||||||
|
CountAsOne: ['array', 'heredoc', 'method_call']
|
||||||
|
Max: 20 # Override default of 5
|
||||||
|
|
||||||
|
RSpec/MultipleExpectations:
|
||||||
|
Max: 10 # Overrides default of 1
|
||||||
|
|
||||||
|
RSpec/MultipleMemoizedHelpers:
|
||||||
|
Max: 20 # Overrides default of 5
|
||||||
|
|
||||||
|
RSpec/NamedSubject:
|
||||||
|
EnforcedStyle: named_only
|
||||||
|
|
||||||
|
RSpec/NestedGroups:
|
||||||
|
Max: 10 # Overrides default of 3
|
||||||
|
|
||||||
|
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
|
|
@ -27,21 +27,6 @@ Metrics/CyclomaticComplexity:
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 27
|
Max: 27
|
||||||
|
|
||||||
# Configuration parameters: CountAsOne.
|
|
||||||
RSpec/ExampleLength:
|
|
||||||
Max: 18
|
|
||||||
|
|
||||||
RSpec/MultipleExpectations:
|
|
||||||
Max: 7
|
|
||||||
|
|
||||||
# Configuration parameters: AllowSubject.
|
|
||||||
RSpec/MultipleMemoizedHelpers:
|
|
||||||
Max: 17
|
|
||||||
|
|
||||||
# Configuration parameters: AllowedGroups.
|
|
||||||
RSpec/NestedGroups:
|
|
||||||
Max: 6
|
|
||||||
|
|
||||||
Rails/OutputSafety:
|
Rails/OutputSafety:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/initializers/simple_form.rb'
|
- 'config/initializers/simple_form.rb'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.3.2
|
3.3.3
|
||||||
|
|
98
Dockerfile
98
Dockerfile
|
@ -1,5 +1,8 @@
|
||||||
# syntax=docker/dockerfile:1.7
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
# This file is designed for production server deployment, not local development work
|
||||||
|
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
|
||||||
|
|
||||||
# Please see https://docs.docker.com/engine/reference/builder for information about
|
# Please see https://docs.docker.com/engine/reference/builder for information about
|
||||||
# the extended buildx capabilities used in this file.
|
# the extended buildx capabilities used in this file.
|
||||||
# Make sure multiarch TARGETPLATFORM is available for interpolation
|
# Make sure multiarch TARGETPLATFORM is available for interpolation
|
||||||
|
@ -7,22 +10,24 @@
|
||||||
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.3.1"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
||||||
ARG RUBY_VERSION="3.3.1"
|
# renovate: datasource=docker depName=docker.io/ruby
|
||||||
|
ARG RUBY_VERSION="3.3.3"
|
||||||
# # 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"]
|
||||||
|
# renovate: datasource=node-version depName=node
|
||||||
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.3.1-slim-bookworm)
|
# Ruby image to use for base image based on combined variables (ex: 3.3.x-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
|
||||||
# Example: v4.2.0-nightly.2023.11.09+something
|
# Example: v4.2.0-nightly.2023.11.09+something
|
||||||
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
|
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
|
||||||
ARG MASTODON_VERSION_PRERELEASE="bark"
|
ARG MASTODON_VERSION_PRERELEASE="bark"
|
||||||
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"]
|
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-12345"]
|
||||||
ARG MASTODON_VERSION_METADATA="dev"
|
ARG MASTODON_VERSION_METADATA="dev"
|
||||||
|
|
||||||
# Allow Ruby on Rails to serve static files
|
# Allow Ruby on Rails to serve static files
|
||||||
|
@ -60,7 +65,9 @@ ENV \
|
||||||
DEBIAN_FRONTEND="noninteractive" \
|
DEBIAN_FRONTEND="noninteractive" \
|
||||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
||||||
# Optimize jemalloc 5.x performance
|
# Optimize jemalloc 5.x performance
|
||||||
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0"
|
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
||||||
|
# Enable libvips, should not be changed
|
||||||
|
MASTODON_USE_LIBVIPS=true
|
||||||
|
|
||||||
# Set default shell used for running commands
|
# Set default shell used for running commands
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
|
SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
|
||||||
|
@ -97,7 +104,6 @@ RUN \
|
||||||
curl \
|
curl \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
file \
|
file \
|
||||||
imagemagick \
|
|
||||||
libjemalloc2 \
|
libjemalloc2 \
|
||||||
patchelf \
|
patchelf \
|
||||||
procps \
|
procps \
|
||||||
|
@ -131,18 +137,31 @@ RUN \
|
||||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||||
# Install build tools and bundler dependencies from APT
|
# Install build tools and bundler dependencies from APT
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
g++ \
|
build-essential \
|
||||||
gcc \
|
|
||||||
git \
|
git \
|
||||||
libgdbm-dev \
|
libgdbm-dev \
|
||||||
|
libglib2.0-dev \
|
||||||
libgmp-dev \
|
libgmp-dev \
|
||||||
libicu-dev \
|
libicu-dev \
|
||||||
libidn-dev \
|
libidn-dev \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
make \
|
meson \
|
||||||
|
pkg-config \
|
||||||
shared-mime-info \
|
shared-mime-info \
|
||||||
zlib1g-dev \
|
# libvips components
|
||||||
|
libcgif-dev \
|
||||||
|
libexif-dev \
|
||||||
|
libexpat1-dev \
|
||||||
|
libgirepository1.0-dev \
|
||||||
|
libheif-dev \
|
||||||
|
libimagequant-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
liblcms2-dev \
|
||||||
|
liborc-dev \
|
||||||
|
libspng-dev \
|
||||||
|
libtiff-dev \
|
||||||
|
libwebp-dev \
|
||||||
;
|
;
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
|
@ -151,6 +170,26 @@ RUN \
|
||||||
corepack enable; \
|
corepack enable; \
|
||||||
corepack prepare --activate;
|
corepack prepare --activate;
|
||||||
|
|
||||||
|
# Create temporary libvips specific build layer from build layer
|
||||||
|
FROM build as libvips
|
||||||
|
|
||||||
|
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
||||||
|
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
||||||
|
ARG VIPS_VERSION=8.15.2
|
||||||
|
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
||||||
|
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
||||||
|
|
||||||
|
WORKDIR /usr/local/libvips/src
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
curl -sSL -o vips-${VIPS_VERSION}.tar.xz ${VIPS_URL}/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.xz; \
|
||||||
|
tar xf vips-${VIPS_VERSION}.tar.xz; \
|
||||||
|
cd vips-${VIPS_VERSION}; \
|
||||||
|
meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false; \
|
||||||
|
cd build; \
|
||||||
|
ninja; \
|
||||||
|
ninja install;
|
||||||
|
|
||||||
# Create temporary bundler specific build layer from build layer
|
# Create temporary bundler specific build layer from build layer
|
||||||
FROM build as bundler
|
FROM build as bundler
|
||||||
|
|
||||||
|
@ -200,16 +239,16 @@ COPY . /opt/mastodon/
|
||||||
COPY --from=yarn /opt/mastodon /opt/mastodon/
|
COPY --from=yarn /opt/mastodon /opt/mastodon/
|
||||||
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
||||||
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
||||||
|
# Copy libvips components to layer for precompiler
|
||||||
|
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
|
||||||
|
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
|
ldconfig; \
|
||||||
# Use Ruby on Rails to create Mastodon assets
|
# Use Ruby on Rails to create Mastodon assets
|
||||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=precompile_placeholder \
|
SECRET_KEY_BASE_DUMMY=1 \
|
||||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=precompile_placeholder \
|
|
||||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=precompile_placeholder \
|
|
||||||
OTP_SECRET=precompile_placeholder \
|
|
||||||
SECRET_KEY_BASE=precompile_placeholder \
|
|
||||||
bundle exec rails assets:precompile; \
|
bundle exec rails assets:precompile; \
|
||||||
# Cleanup temporary files
|
# Cleanup temporary files
|
||||||
rm -fr /opt/mastodon/tmp;
|
rm -fr /opt/mastodon/tmp;
|
||||||
|
@ -229,12 +268,27 @@ RUN \
|
||||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||||
# Apt update install non-dev versions of necessary components
|
# Apt update install non-dev versions of necessary components
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
libssl3 \
|
libexpat1 \
|
||||||
libpq5 \
|
libglib2.0-0 \
|
||||||
libicu72 \
|
libicu72 \
|
||||||
libidn12 \
|
libidn12 \
|
||||||
|
libpq5 \
|
||||||
libreadline8 \
|
libreadline8 \
|
||||||
|
libssl3 \
|
||||||
libyaml-0-2 \
|
libyaml-0-2 \
|
||||||
|
# libvips components
|
||||||
|
libcgif0 \
|
||||||
|
libexif12 \
|
||||||
|
libheif1 \
|
||||||
|
libimagequant0 \
|
||||||
|
libjpeg62-turbo \
|
||||||
|
liblcms2-2 \
|
||||||
|
liborc-0.4-0 \
|
||||||
|
libspng0 \
|
||||||
|
libtiff6 \
|
||||||
|
libwebp7 \
|
||||||
|
libwebpdemux2 \
|
||||||
|
libwebpmux3 \
|
||||||
;
|
;
|
||||||
|
|
||||||
# Copy Mastodon sources into final layer
|
# Copy Mastodon sources into final layer
|
||||||
|
@ -245,9 +299,17 @@ COPY --from=precompiler /opt/mastodon/public/packs /opt/mastodon/public/packs
|
||||||
COPY --from=precompiler /opt/mastodon/public/assets /opt/mastodon/public/assets
|
COPY --from=precompiler /opt/mastodon/public/assets /opt/mastodon/public/assets
|
||||||
# Copy bundler components to layer
|
# Copy bundler components to layer
|
||||||
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
||||||
|
# Copy libvips components to layer
|
||||||
|
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
|
||||||
|
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Precompile bootsnap code for faster Rails startup
|
ldconfig; \
|
||||||
|
# Smoketest media processors
|
||||||
|
vips -v;
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Precompile bootsnap code for faster Rails startup
|
||||||
bundle exec bootsnap precompile --gemfile app/ lib/;
|
bundle exec bootsnap precompile --gemfile app/ lib/;
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
|
|
6
Gemfile
6
Gemfile
|
@ -23,6 +23,7 @@ gem 'fog-core', '<= 2.4.0'
|
||||||
gem 'fog-openstack', '~> 1.0', require: false
|
gem 'fog-openstack', '~> 1.0', require: false
|
||||||
gem 'kt-paperclip', '~> 7.2'
|
gem 'kt-paperclip', '~> 7.2'
|
||||||
gem 'md-paperclip-azure', '~> 2.2', require: false
|
gem 'md-paperclip-azure', '~> 2.2', require: false
|
||||||
|
gem 'ruby-vips', '~> 2.2', require: false
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.8'
|
||||||
|
@ -56,7 +57,7 @@ gem 'hiredis', '~> 0.6'
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 5.2.0'
|
gem 'http', '~> 5.2.0'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.6.2'
|
gem 'httplog', '~> 1.7.0'
|
||||||
gem 'i18n'
|
gem 'i18n'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'inline_svg'
|
gem 'inline_svg'
|
||||||
|
@ -106,7 +107,7 @@ gem 'private_address_check', '~> 0.5'
|
||||||
gem 'opentelemetry-api', '~> 1.2.5'
|
gem 'opentelemetry-api', '~> 1.2.5'
|
||||||
|
|
||||||
group :opentelemetry do
|
group :opentelemetry do
|
||||||
gem 'opentelemetry-exporter-otlp', '~> 0.26.3', require: false
|
gem 'opentelemetry-exporter-otlp', '~> 0.27.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
||||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
||||||
|
@ -170,6 +171,7 @@ group :development do
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
gem 'rubocop-rails', require: false
|
gem 'rubocop-rails', require: false
|
||||||
gem 'rubocop-rspec', require: false
|
gem 'rubocop-rspec', require: false
|
||||||
|
gem 'rubocop-rspec_rails', require: false
|
||||||
|
|
||||||
# Annotates modules with schema
|
# Annotates modules with schema
|
||||||
gem 'annotate', '~> 3.2'
|
gem 'annotate', '~> 3.2'
|
||||||
|
|
187
Gemfile.lock
187
Gemfile.lock
|
@ -10,35 +10,35 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.1.3.3)
|
actioncable (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
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.3)
|
actionmailbox (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.3)
|
activejob (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.3)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.3)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.1.3.3)
|
actionmailer (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
actionview (= 7.1.3.3)
|
actionview (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.3)
|
activejob (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
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.3)
|
actionpack (7.1.3.4)
|
||||||
actionview (= 7.1.3.3)
|
actionview (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
racc
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
|
@ -46,15 +46,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.3)
|
actiontext (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.3)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.3)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.1.3.3)
|
actionview (7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
|
@ -64,22 +64,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.3)
|
activejob (7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.1.3.3)
|
activemodel (7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
activerecord (7.1.3.3)
|
activerecord (7.1.3.4)
|
||||||
activemodel (= 7.1.3.3)
|
activemodel (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (7.1.3.3)
|
activestorage (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.3)
|
activejob (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.3)
|
activerecord (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (7.1.3.3)
|
activesupport (7.1.3.4)
|
||||||
base64
|
base64
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
@ -100,17 +100,17 @@ GEM
|
||||||
attr_required (1.0.2)
|
attr_required (1.0.2)
|
||||||
awrence (1.2.1)
|
awrence (1.2.1)
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.929.0)
|
aws-partitions (1.940.0)
|
||||||
aws-sdk-core (3.196.1)
|
aws-sdk-core (3.197.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.81.0)
|
aws-sdk-kms (1.83.0)
|
||||||
aws-sdk-core (~> 3, >= 3.193.0)
|
aws-sdk-core (~> 3, >= 3.197.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.151.0)
|
aws-sdk-s3 (1.152.3)
|
||||||
aws-sdk-core (~> 3, >= 3.194.0)
|
aws-sdk-core (~> 3, >= 3.197.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.8.0)
|
aws-sigv4 (1.8.0)
|
||||||
|
@ -168,7 +168,7 @@ GEM
|
||||||
climate_control (1.2.0)
|
climate_control (1.2.0)
|
||||||
cocoon (1.2.15)
|
cocoon (1.2.15)
|
||||||
color_diff (0.1)
|
color_diff (0.1)
|
||||||
concurrent-ruby (1.3.1)
|
concurrent-ruby (1.3.3)
|
||||||
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)
|
||||||
|
@ -272,7 +272,7 @@ GEM
|
||||||
fog-json (1.2.0)
|
fog-json (1.2.0)
|
||||||
fog-core
|
fog-core
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
fog-openstack (1.1.1)
|
fog-openstack (1.1.3)
|
||||||
fog-core (~> 2.1)
|
fog-core (~> 2.1)
|
||||||
fog-json (>= 1.0)
|
fog-json (>= 1.0)
|
||||||
formatador (1.1.0)
|
formatador (1.1.0)
|
||||||
|
@ -321,7 +321,7 @@ GEM
|
||||||
http-form_data (2.3.0)
|
http-form_data (2.3.0)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
httplog (1.6.3)
|
httplog (1.7.0)
|
||||||
rack (>= 2.0)
|
rack (>= 2.0)
|
||||||
rainbow (>= 2.0.0)
|
rainbow (>= 2.0.0)
|
||||||
i18n (1.14.5)
|
i18n (1.14.5)
|
||||||
|
@ -422,9 +422,9 @@ GEM
|
||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
mime-types (3.5.2)
|
mime-types (3.5.2)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2024.0507)
|
mime-types-data (3.2024.0604)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.6)
|
mini_portile2 (2.8.7)
|
||||||
minitest (5.23.1)
|
minitest (5.23.1)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.2)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
|
@ -434,7 +434,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.11)
|
net-imap (0.4.12)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
|
@ -445,7 +445,7 @@ GEM
|
||||||
net-smtp (0.5.0)
|
net-smtp (0.5.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.3)
|
nio4r (2.7.3)
|
||||||
nokogiri (1.16.5)
|
nokogiri (1.16.6)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nsa (0.3.0)
|
nsa (0.3.0)
|
||||||
|
@ -453,7 +453,7 @@ GEM
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sidekiq (>= 3.5)
|
sidekiq (>= 3.5)
|
||||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||||
oj (3.16.3)
|
oj (3.16.4)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
omniauth (2.1.2)
|
omniauth (2.1.2)
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
|
@ -489,7 +489,7 @@ GEM
|
||||||
opentelemetry-api (1.2.5)
|
opentelemetry-api (1.2.5)
|
||||||
opentelemetry-common (0.20.1)
|
opentelemetry-common (0.20.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-exporter-otlp (0.26.3)
|
opentelemetry-exporter-otlp (0.27.0)
|
||||||
google-protobuf (~> 3.14)
|
google-protobuf (~> 3.14)
|
||||||
googleapis-common-protos-types (~> 1.3)
|
googleapis-common-protos-types (~> 1.3)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
|
@ -498,6 +498,10 @@ GEM
|
||||||
opentelemetry-semantic_conventions
|
opentelemetry-semantic_conventions
|
||||||
opentelemetry-helpers-sql-obfuscation (0.1.0)
|
opentelemetry-helpers-sql-obfuscation (0.1.0)
|
||||||
opentelemetry-common (~> 0.20)
|
opentelemetry-common (~> 0.20)
|
||||||
|
opentelemetry-instrumentation-action_mailer (0.1.0)
|
||||||
|
opentelemetry-api (~> 1.0)
|
||||||
|
opentelemetry-instrumentation-active_support (~> 0.1)
|
||||||
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-action_pack (0.9.0)
|
opentelemetry-instrumentation-action_pack (0.9.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
|
@ -551,8 +555,9 @@ GEM
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-common (~> 0.20.0)
|
opentelemetry-common (~> 0.20.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-rails (0.30.1)
|
opentelemetry-instrumentation-rails (0.30.2)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
|
opentelemetry-instrumentation-action_mailer (~> 0.1.0)
|
||||||
opentelemetry-instrumentation-action_pack (~> 0.9.0)
|
opentelemetry-instrumentation-action_pack (~> 0.9.0)
|
||||||
opentelemetry-instrumentation-action_view (~> 0.7.0)
|
opentelemetry-instrumentation-action_view (~> 0.7.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.7.0)
|
opentelemetry-instrumentation-active_job (~> 0.7.0)
|
||||||
|
@ -578,15 +583,15 @@ GEM
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ox (2.14.18)
|
ox (2.14.18)
|
||||||
parallel (1.24.0)
|
parallel (1.25.1)
|
||||||
parser (3.3.2.0)
|
parser (3.3.3.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pastel (0.8.0)
|
pastel (0.8.0)
|
||||||
tty-color (~> 0.5)
|
tty-color (~> 0.5)
|
||||||
pg (1.5.6)
|
pg (1.5.6)
|
||||||
pghero (3.4.1)
|
pghero (3.5.0)
|
||||||
activerecord (>= 6)
|
activerecord (>= 6)
|
||||||
premailer (1.23.0)
|
premailer (1.23.0)
|
||||||
addressable
|
addressable
|
||||||
|
@ -634,20 +639,20 @@ GEM
|
||||||
rackup (1.0.0)
|
rackup (1.0.0)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (7.1.3.3)
|
rails (7.1.3.4)
|
||||||
actioncable (= 7.1.3.3)
|
actioncable (= 7.1.3.4)
|
||||||
actionmailbox (= 7.1.3.3)
|
actionmailbox (= 7.1.3.4)
|
||||||
actionmailer (= 7.1.3.3)
|
actionmailer (= 7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
actiontext (= 7.1.3.3)
|
actiontext (= 7.1.3.4)
|
||||||
actionview (= 7.1.3.3)
|
actionview (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.3)
|
activejob (= 7.1.3.4)
|
||||||
activemodel (= 7.1.3.3)
|
activemodel (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.3)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.3)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.1.3.3)
|
railties (= 7.1.3.4)
|
||||||
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)
|
||||||
|
@ -662,9 +667,9 @@ GEM
|
||||||
rails-i18n (7.0.9)
|
rails-i18n (7.0.9)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (7.1.3.3)
|
railties (7.1.3.4)
|
||||||
actionpack (= 7.1.3.3)
|
actionpack (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.3)
|
activesupport (= 7.1.3.4)
|
||||||
irb
|
irb
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
|
@ -686,15 +691,15 @@ GEM
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.9.2)
|
regexp_parser (2.9.2)
|
||||||
reline (0.5.7)
|
reline (0.5.8)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
request_store (1.6.0)
|
request_store (1.6.0)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.1.1)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
rexml (3.2.8)
|
rexml (3.3.0)
|
||||||
strscan (>= 3.0.9)
|
strscan
|
||||||
rotp (6.3.0)
|
rotp (6.3.0)
|
||||||
rouge (4.2.1)
|
rouge (4.2.1)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
|
@ -739,9 +744,7 @@ GEM
|
||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.31.3)
|
rubocop-ast (1.31.3)
|
||||||
parser (>= 3.3.1.0)
|
parser (>= 3.3.1.0)
|
||||||
rubocop-capybara (2.20.0)
|
rubocop-capybara (2.21.0)
|
||||||
rubocop (~> 1.41)
|
|
||||||
rubocop-factory_bot (2.25.1)
|
|
||||||
rubocop (~> 1.41)
|
rubocop (~> 1.41)
|
||||||
rubocop-performance (1.21.0)
|
rubocop-performance (1.21.0)
|
||||||
rubocop (>= 1.48.1, < 2.0)
|
rubocop (>= 1.48.1, < 2.0)
|
||||||
|
@ -751,25 +754,25 @@ GEM
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.33.0, < 2.0)
|
rubocop (>= 1.33.0, < 2.0)
|
||||||
rubocop-ast (>= 1.31.1, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rspec (2.29.2)
|
rubocop-rspec (3.0.1)
|
||||||
rubocop (~> 1.40)
|
rubocop (~> 1.61)
|
||||||
rubocop-capybara (~> 2.17)
|
rubocop-rspec_rails (2.30.0)
|
||||||
rubocop-factory_bot (~> 2.22)
|
rubocop (~> 1.61)
|
||||||
rubocop-rspec_rails (~> 2.28)
|
rubocop-rspec (~> 3, >= 3.0.1)
|
||||||
rubocop-rspec_rails (2.28.3)
|
|
||||||
rubocop (~> 1.40)
|
|
||||||
ruby-prof (1.7.0)
|
ruby-prof (1.7.0)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-saml (1.16.0)
|
ruby-saml (1.16.0)
|
||||||
nokogiri (>= 1.13.10)
|
nokogiri (>= 1.13.10)
|
||||||
rexml
|
rexml
|
||||||
|
ruby-vips (2.2.1)
|
||||||
|
ffi (~> 1.12)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
rufus-scheduler (3.9.1)
|
rufus-scheduler (3.9.1)
|
||||||
fugit (~> 1.1, >= 1.1.6)
|
fugit (~> 1.1, >= 1.1.6)
|
||||||
safety_net_attestation (0.4.0)
|
safety_net_attestation (0.4.0)
|
||||||
jwt (~> 2.0)
|
jwt (~> 2.0)
|
||||||
sanitize (6.1.0)
|
sanitize (6.1.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
scenic (1.8.0)
|
scenic (1.8.0)
|
||||||
|
@ -895,7 +898,7 @@ GEM
|
||||||
xorcist (1.1.3)
|
xorcist (1.1.3)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.6.14)
|
zeitwerk (2.6.15)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -945,7 +948,7 @@ DEPENDENCIES
|
||||||
htmlentities (~> 4.3)
|
htmlentities (~> 4.3)
|
||||||
http (~> 5.2.0)
|
http (~> 5.2.0)
|
||||||
http_accept_language (~> 2.1)
|
http_accept_language (~> 2.1)
|
||||||
httplog (~> 1.6.2)
|
httplog (~> 1.7.0)
|
||||||
i18n
|
i18n
|
||||||
i18n-tasks (~> 1.0)
|
i18n-tasks (~> 1.0)
|
||||||
idn-ruby
|
idn-ruby
|
||||||
|
@ -976,7 +979,7 @@ DEPENDENCIES
|
||||||
omniauth-saml (~> 2.0)
|
omniauth-saml (~> 2.0)
|
||||||
omniauth_openid_connect (~> 0.6.1)
|
omniauth_openid_connect (~> 0.6.1)
|
||||||
opentelemetry-api (~> 1.2.5)
|
opentelemetry-api (~> 1.2.5)
|
||||||
opentelemetry-exporter-otlp (~> 0.26.3)
|
opentelemetry-exporter-otlp (~> 0.27.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.7.1)
|
opentelemetry-instrumentation-active_job (~> 0.7.1)
|
||||||
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
|
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
|
||||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
|
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
|
||||||
|
@ -1021,8 +1024,10 @@ DEPENDENCIES
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
rubocop-rails
|
rubocop-rails
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
|
rubocop-rspec_rails
|
||||||
ruby-prof
|
ruby-prof
|
||||||
ruby-progressbar (~> 1.13)
|
ruby-progressbar (~> 1.13)
|
||||||
|
ruby-vips (~> 2.2)
|
||||||
rubyzip (~> 2.3)
|
rubyzip (~> 2.3)
|
||||||
sanitize (~> 6.0)
|
sanitize (~> 6.0)
|
||||||
scenic (~> 1.7)
|
scenic (~> 1.7)
|
||||||
|
@ -1050,7 +1055,7 @@ DEPENDENCIES
|
||||||
xorcist (~> 1.1)
|
xorcist (~> 1.1)
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.3.1p55
|
ruby 3.3.2p78
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.9
|
2.5.11
|
||||||
|
|
68
README.md
68
README.md
|
@ -62,7 +62,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
||||||
### Tech stack
|
### Tech stack
|
||||||
|
|
||||||
- **Ruby on Rails** powers the REST API and other web pages
|
- **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
|
- **Node.js** powers the streaming API
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
@ -72,7 +72,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
||||||
- **Ruby** 3.1+
|
- **Ruby** 3.1+
|
||||||
- **Node.js** 18+
|
- **Node.js** 18+
|
||||||
|
|
||||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
@ -86,40 +86,51 @@ A **Vagrant** configuration is included for development purposes. To use it, com
|
||||||
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
|
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
|
||||||
- Open `http://mastodon.local` in your browser
|
- Open `http://mastodon.local` in your browser
|
||||||
|
|
||||||
### MacOS
|
### macOS
|
||||||
|
|
||||||
To set up **MacOS** for native development, complete the following steps:
|
To set up **macOS** for native development, complete the following steps:
|
||||||
|
|
||||||
- Use a Ruby version manager to install the specified version from `.ruby-version`
|
- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick
|
||||||
- Run `bundle` to install required gems
|
libidn nvm` to install the required project dependencies
|
||||||
- Run `brew install postgresql@14 redis imagemagick libidn` to install required dependencies
|
- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
|
||||||
- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from `.nvmrc`
|
`nvm use` to activate the node version from `.nvmrc`
|
||||||
- Run `yarn` to install required packages
|
- Run the `bin/setup` script, which will install the required ruby gems and node
|
||||||
- Run `corepack enable && corepack prepare`
|
packages and prepare the database for local development
|
||||||
- Run `RAILS_ENV=development bundle exec rails db:setup`
|
- Finally, run the `bin/dev` script which will launch services via `overmind`
|
||||||
- Finally, run `bin/dev` which will launch the local services via `overmind` (if installed) or `foreman`
|
(if installed) or `foreman`
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
For development with **Docker**, complete the following steps:
|
For production hosting and deployment with **Docker**, use the `Dockerfile` and
|
||||||
|
`docker-compose.yml` in the project root directory.
|
||||||
|
|
||||||
- Install Docker Desktop
|
For local development, install and launch [Docker], and run:
|
||||||
- Run `docker compose -f .devcontainer/docker-compose.yml up -d`
|
|
||||||
- Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh`
|
|
||||||
- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app bin/dev`
|
|
||||||
|
|
||||||
If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers).
|
```shell
|
||||||
|
docker compose -f .devcontainer/compose.yaml up -d
|
||||||
|
docker compose -f .devcontainer/compose.yaml exec app bin/setup
|
||||||
|
docker compose -f .devcontainer/compose.yaml exec app bin/dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dev Containers
|
||||||
|
|
||||||
|
Within IDEs that support the [Development Containers] specification, start the
|
||||||
|
"Mastodon on local machine" container from the editor. The necessary `docker
|
||||||
|
compose` commands to build and setup the container should run automatically. For
|
||||||
|
**Visual Studio Code** this requires installing the [Dev Container extension].
|
||||||
|
|
||||||
### GitHub Codespaces
|
### GitHub Codespaces
|
||||||
|
|
||||||
To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project..
|
[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
|
||||||
|
development environment configured with the software needed for this project.
|
||||||
|
|
||||||
- Click this button to create a new codespace:<br>
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace]
|
||||||
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json)
|
|
||||||
- Wait for the environment to build. This will take a few minutes.
|
- Click the button to create a new codespace, and confirm the options
|
||||||
- When the editor is ready, run `bin/dev` in the terminal.
|
- Wait for the environment to build (takes a few minutes)
|
||||||
- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon.
|
- When the editor is ready, run `bin/dev` in the terminal
|
||||||
- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_.
|
- Wait for an _Open in Browser_ prompt. This will open Mastodon
|
||||||
|
- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -138,3 +149,10 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json
|
||||||
|
[Dev Container extension]: https://containers.dev/supporting#dev-containers
|
||||||
|
[Development Containers]: https://containers.dev/supporting
|
||||||
|
[Docker]: https://docs.docker.com
|
||||||
|
[GitHub Codespaces]: https://docs.github.com/en/codespaces
|
||||||
|
[Homebrew]: https://brew.sh
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
|
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
|
||||||
|
|
||||||
- open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
|
- open a [GitHub security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
|
||||||
- reach us at <security@joinmastodon.org>
|
- reach us at <security@joinmastodon.org>
|
||||||
|
|
||||||
You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
|
You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
|
||||||
|
|
6
Vagrantfile
vendored
6
Vagrantfile
vendored
|
@ -151,6 +151,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.vm.provider :libvirt do |libvirt|
|
||||||
|
libvirt.cpus = 3
|
||||||
|
libvirt.memory = 8192
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||||
# access the development site at http://mastodon.local.
|
# access the development site at http://mastodon.local.
|
||||||
# If you change it, also change it in .env.vagrant before provisioning
|
# If you change it, also change it in .env.vagrant before provisioning
|
||||||
|
|
|
@ -4,6 +4,18 @@ module Admin
|
||||||
class DomainBlocksController < BaseController
|
class DomainBlocksController < BaseController
|
||||||
before_action :set_domain_block, only: [:destroy, :edit, :update]
|
before_action :set_domain_block, only: [:destroy, :edit, :update]
|
||||||
|
|
||||||
|
PERMITTED_PARAMS = %i(
|
||||||
|
domain
|
||||||
|
obfuscate
|
||||||
|
private_comment
|
||||||
|
public_comment
|
||||||
|
reject_media
|
||||||
|
reject_reports
|
||||||
|
severity
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
PERMITTED_UPDATE_PARAMS = PERMITTED_PARAMS.without(:domain).freeze
|
||||||
|
|
||||||
def batch
|
def batch
|
||||||
authorize :domain_block, :create?
|
authorize :domain_block, :create?
|
||||||
@form = Form::DomainBlockBatch.new(form_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
@form = Form::DomainBlockBatch.new(form_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||||
|
@ -88,11 +100,17 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
params
|
||||||
|
.require(:domain_block)
|
||||||
|
.slice(*PERMITTED_UPDATE_PARAMS)
|
||||||
|
.permit(*PERMITTED_UPDATE_PARAMS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
params
|
||||||
|
.require(:domain_block)
|
||||||
|
.slice(*PERMITTED_PARAMS)
|
||||||
|
.permit(*PERMITTED_PARAMS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def form_domain_block_batch_params
|
def form_domain_block_batch_params
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts', :'read:me' }, except: [:update]
|
before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }, except: [:update]
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@ class Api::V1::Admin::TagsController < Api::BaseController
|
||||||
|
|
||||||
LIMIT = 100
|
LIMIT = 100
|
||||||
|
|
||||||
|
PERMITTED_PARAMS = %i(
|
||||||
|
display_name
|
||||||
|
listable
|
||||||
|
trendable
|
||||||
|
usable
|
||||||
|
).freeze
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :tag, :index?
|
authorize :tag, :index?
|
||||||
render json: @tags, each_serializer: REST::Admin::TagSerializer
|
render json: @tags, each_serializer: REST::Admin::TagSerializer
|
||||||
|
@ -40,7 +47,9 @@ class Api::V1::Admin::TagsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_params
|
def tag_params
|
||||||
params.permit(:display_name, :trendable, :usable, :listable)
|
params
|
||||||
|
.slice(*PERMITTED_PARAMS)
|
||||||
|
.permit(*PERMITTED_PARAMS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
|
|
52
app/controllers/api/v1/timelines/link_controller.rb
Normal file
52
app/controllers/api/v1/timelines/link_controller.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
|
||||||
|
before_action :set_preview_card
|
||||||
|
before_action :set_statuses
|
||||||
|
|
||||||
|
PERMITTED_PARAMS = %i(
|
||||||
|
url
|
||||||
|
limit
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
def show
|
||||||
|
cache_if_unauthenticated!
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def require_auth?
|
||||||
|
!Setting.timeline_preview
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_preview_card
|
||||||
|
@preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_statuses
|
||||||
|
@statuses = @preview_card.nil? ? [] : preload_collection(link_timeline_statuses, Status)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_timeline_statuses
|
||||||
|
link_feed.get(
|
||||||
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
|
params[:max_id],
|
||||||
|
params[:since_id],
|
||||||
|
params[:min_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_feed
|
||||||
|
LinkFeed.new(@preview_card, current_account)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_timelines_link_url next_path_params
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_timelines_link_url prev_path_params
|
||||||
|
end
|
||||||
|
end
|
91
app/controllers/api/v2_alpha/notifications_controller.rb
Normal file
91
app/controllers/api/v2_alpha/notifications_controller.rb
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V2Alpha::NotificationsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
|
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
|
||||||
|
before_action :require_user!
|
||||||
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
|
DEFAULT_NOTIFICATIONS_LIMIT = 40
|
||||||
|
|
||||||
|
def index
|
||||||
|
with_read_replica do
|
||||||
|
@notifications = load_notifications
|
||||||
|
@group_metadata = load_group_metadata
|
||||||
|
@relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@notification = current_account.notifications.without_suspended.find_by!(group_key: params[:id])
|
||||||
|
render json: NotificationGroup.from_notification(@notification), serializer: REST::NotificationGroupSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
current_account.notifications.delete_all
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
def dismiss
|
||||||
|
current_account.notifications.where(group_key: params[:id]).destroy_all
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_notifications
|
||||||
|
notifications = browserable_account_notifications.includes(from_account: [:account_stat, :user]).to_a_grouped_paginated_by_id(
|
||||||
|
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||||
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
||||||
|
preload_collection(target_statuses, Status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_group_metadata
|
||||||
|
return {} if @notifications.empty?
|
||||||
|
|
||||||
|
browserable_account_notifications
|
||||||
|
.where(group_key: @notifications.filter_map(&:group_key))
|
||||||
|
.where(id: (@notifications.last.id)..(@notifications.first.id))
|
||||||
|
.group(:group_key)
|
||||||
|
.pluck(:group_key, 'min(notifications.id) as min_id', 'max(notifications.id) as max_id', 'max(notifications.created_at) as latest_notification_at')
|
||||||
|
.to_h { |group_key, min_id, max_id, latest_notification_at| [group_key, { min_id: min_id, max_id: max_id, latest_notification_at: latest_notification_at }] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def browserable_account_notifications
|
||||||
|
current_account.notifications.without_suspended.browserable(
|
||||||
|
types: Array(browserable_params[:types]),
|
||||||
|
exclude_types: Array(browserable_params[:exclude_types]),
|
||||||
|
include_filtered: truthy_param?(:include_filtered)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def target_statuses_from_notifications
|
||||||
|
@notifications.filter_map(&:target_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v2_alpha_notifications_url pagination_params(max_id: pagination_max_id) unless @notifications.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v2_alpha_notifications_url pagination_params(min_id: pagination_since_id) unless @notifications.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_collection
|
||||||
|
@notifications
|
||||||
|
end
|
||||||
|
|
||||||
|
def browserable_params
|
||||||
|
params.permit(:include_filtered, types: [], exclude_types: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit, :types, :exclude_types, :include_filtered).permit(:limit, :include_filtered, types: [], exclude_types: []).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
|
||||||
def new
|
def new
|
||||||
@application = Doorkeeper::Application.new(
|
@application = Doorkeeper::Application.new(
|
||||||
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
||||||
scopes: 'read:me'
|
scopes: 'profile'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,15 @@ module Admin::ActionLogsHelper
|
||||||
link_to log.human_identifier, admin_roles_path(log.target_id)
|
link_to log.human_identifier, admin_roles_path(log.target_id)
|
||||||
when 'Report'
|
when 'Report'
|
||||||
link_to "##{log.human_identifier.presence || log.target_id}", admin_report_path(log.target_id)
|
link_to "##{log.human_identifier.presence || log.target_id}", admin_report_path(log.target_id)
|
||||||
when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain'
|
when 'Instance', 'DomainBlock', 'DomainAllow', 'UnavailableDomain'
|
||||||
link_to log.human_identifier, "https://#{log.human_identifier.presence}"
|
log.human_identifier.present? ? link_to(log.human_identifier, admin_instance_path(log.human_identifier)) : I18n.t('admin.action_logs.unavailable_instance')
|
||||||
when 'Status'
|
when 'Status'
|
||||||
link_to log.human_identifier, log.permalink
|
link_to log.human_identifier, log.permalink
|
||||||
when 'AccountWarning'
|
when 'AccountWarning'
|
||||||
link_to log.human_identifier, disputes_strike_path(log.target_id)
|
link_to log.human_identifier, disputes_strike_path(log.target_id)
|
||||||
when 'Announcement'
|
when 'Announcement'
|
||||||
link_to truncate(log.human_identifier), edit_admin_announcement_path(log.target_id)
|
link_to truncate(log.human_identifier), edit_admin_announcement_path(log.target_id)
|
||||||
when 'IpBlock', 'Instance', 'CustomEmoji'
|
when 'IpBlock', 'EmailDomainBlock', 'CustomEmoji'
|
||||||
log.human_identifier
|
log.human_identifier
|
||||||
when 'CanonicalEmailBlock'
|
when 'CanonicalEmailBlock'
|
||||||
content_tag(:samp, (log.human_identifier.presence || '')[0...7], title: log.human_identifier)
|
content_tag(:samp, (log.human_identifier.presence || '')[0...7], title: log.human_identifier)
|
||||||
|
|
|
@ -68,13 +68,17 @@ export function importFetchedStatuses(statuses) {
|
||||||
status.filtered.forEach(result => pushUnique(filters, result.filter));
|
status.filtered.forEach(result => pushUnique(filters, result.filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.reblog && status.reblog.id) {
|
if (status.reblog?.id) {
|
||||||
processStatus(status.reblog);
|
processStatus(status.reblog);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.poll && status.poll.id) {
|
if (status.poll?.id) {
|
||||||
pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', 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);
|
statuses.forEach(processStatus);
|
||||||
|
|
|
@ -36,6 +36,10 @@ export function normalizeStatus(status, normalOldStatus) {
|
||||||
normalStatus.poll = status.poll.id;
|
normalStatus.poll = status.poll.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status.card?.author_account) {
|
||||||
|
normalStatus.card = { ...status.card, author_account: status.card.author_account.id };
|
||||||
|
}
|
||||||
|
|
||||||
if (status.filtered) {
|
if (status.filtered) {
|
||||||
normalStatus.filtered = status.filtered.map(normalizeFilterResult);
|
normalStatus.filtered = status.filtered.map(normalizeFilterResult);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import api, { getLinks } from '../api';
|
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_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';
|
||||||
export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS';
|
export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS';
|
||||||
|
@ -49,8 +49,11 @@ export const fetchTrendingLinks = () => (dispatch) => {
|
||||||
dispatch(fetchTrendingLinksRequest());
|
dispatch(fetchTrendingLinksRequest());
|
||||||
|
|
||||||
api()
|
api()
|
||||||
.get('/api/v1/trends/links')
|
.get('/api/v1/trends/links', { params: { limit: 20 } })
|
||||||
.then(({ data }) => dispatch(fetchTrendingLinksSuccess(data)))
|
.then(({ data }) => {
|
||||||
|
dispatch(importFetchedAccounts(data.map(link => link.author_account).filter(account => !!account)));
|
||||||
|
dispatch(fetchTrendingLinksSuccess(data));
|
||||||
|
})
|
||||||
.catch(err => dispatch(fetchTrendingLinksFail(err)));
|
.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,
|
||||||
|
};
|
|
@ -42,10 +42,12 @@ class ServerBanner extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='server-banner'>
|
<div className='server-banner'>
|
||||||
<div className='server-banner__introduction'>
|
<div className='server-banner__introduction'>
|
||||||
<FormattedMessage id='server_banner.introduction' defaultMessage='{domain} is part of the decentralized social network powered by {mastodon}.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
|
<FormattedMessage id='server_banner.is_one_of_many' defaultMessage='{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' />
|
<Link to='/about'>
|
||||||
|
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' />
|
||||||
|
</Link>
|
||||||
|
|
||||||
<div className='server-banner__description'>
|
<div className='server-banner__description'>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@ -84,10 +86,6 @@ class ServerBanner extends PureComponent {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr className='spacer' />
|
|
||||||
|
|
||||||
<Link className='button button--block button-secondary' to='/about'><FormattedMessage id='server_banner.learn_more' defaultMessage='Learn more' /></Link>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,6 +199,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
timelineId='account'
|
timelineId='account'
|
||||||
|
withCounters
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -110,18 +110,6 @@ class LanguageDropdownMenu extends PureComponent {
|
||||||
}).map(result => result.obj);
|
}).map(result => result.obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
frequentlyUsed () {
|
|
||||||
const { languages, value } = this.props;
|
|
||||||
const current = languages.find(lang => lang[0] === value);
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (current) {
|
|
||||||
results.push(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick = e => {
|
handleClick = e => {
|
||||||
const value = e.currentTarget.getAttribute('data-index');
|
const value = e.currentTarget.getAttribute('data-index');
|
||||||
|
|
||||||
|
|
|
@ -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 PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import { ShortNumber } from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
|
|
||||||
export default class Story extends PureComponent {
|
import { AuthorLink } from './author_link';
|
||||||
|
|
||||||
static propTypes = {
|
const sharesCountRenderer = (displayNumber, pluralReady) => (
|
||||||
url: PropTypes.string,
|
<FormattedMessage
|
||||||
title: PropTypes.string,
|
id='link_preview.shares'
|
||||||
lang: PropTypes.string,
|
defaultMessage='{count, plural, one {{counter} post} other {{counter} posts}}'
|
||||||
publisher: PropTypes.string,
|
values={{
|
||||||
publishedAt: PropTypes.string,
|
count: pluralReady,
|
||||||
author: PropTypes.string,
|
counter: <strong>{displayNumber}</strong>,
|
||||||
sharedTimes: PropTypes.number,
|
}}
|
||||||
thumbnail: PropTypes.string,
|
/>
|
||||||
thumbnailDescription: PropTypes.string,
|
);
|
||||||
blurhash: PropTypes.string,
|
|
||||||
expanded: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
export const Story = ({
|
||||||
thumbnailLoaded: false,
|
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 () {
|
return (
|
||||||
const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, thumbnailDescription, blurhash } = this.props;
|
<div className={classNames('story', { expanded })}>
|
||||||
|
<div className='story__details'>
|
||||||
const { thumbnailLoaded } = this.state;
|
<div className='story__details__publisher'>
|
||||||
|
{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='story__thumbnail'>
|
<a className='story__details__title' lang={lang} href={url} target='blank' rel='noopener'>
|
||||||
{thumbnail ? (
|
{title ? title : <Skeleton />}
|
||||||
<>
|
</a>
|
||||||
<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} />
|
<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 />}
|
||||||
) : <Skeleton />}
|
{typeof sharedTimes === 'number' ? <span className='story__details__shared__pill'><ShortNumber value={sharedTimes} renderer={sharesCountRenderer} /></span> : <Skeleton width='10ch' />}
|
||||||
</div>
|
</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>
|
</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 { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
import Story from './components/story';
|
import { Story } from './components/story';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
links: state.getIn(['trends', 'links', 'items']),
|
links: state.getIn(['trends', 'links', 'items']),
|
||||||
|
@ -75,6 +75,7 @@ class Links extends PureComponent {
|
||||||
publisher={link.get('provider_name')}
|
publisher={link.get('provider_name')}
|
||||||
publishedAt={link.get('published_at')}
|
publishedAt={link.get('published_at')}
|
||||||
author={link.get('author_name')}
|
author={link.get('author_name')}
|
||||||
|
authorAccount={link.getIn(['author_account', 'id'])}
|
||||||
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
||||||
thumbnail={link.get('image')}
|
thumbnail={link.get('image')}
|
||||||
thumbnailDescription={link.get('image_description')}
|
thumbnailDescription={link.get('image_description')}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { PureComponent } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
|
|
||||||
import Immutable from 'immutable';
|
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 DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react';
|
||||||
import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.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 PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { MoreFromAuthor } from 'mastodon/components/more_from_author';
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import { useBlurhash } from 'mastodon/initial_state';
|
import { useBlurhash } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
@ -59,20 +58,6 @@ const addAutoPlay = html => {
|
||||||
return 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 {
|
export default class Card extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -259,7 +244,7 @@ export default class Card extends PureComponent {
|
||||||
{description}
|
{description}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{showAuthor && <MoreFromAuthor author={card.get('author_account')} />}
|
{showAuthor && <MoreFromAuthor accountId={card.get('author_account')} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ const SignInBanner = () => {
|
||||||
if (sso_redirect) {
|
if (sso_redirect) {
|
||||||
return (
|
return (
|
||||||
<div className='sign-in-banner'>
|
<div className='sign-in-banner'>
|
||||||
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
|
<p><strong><FormattedMessage id='sign_in_banner.mastodon_is' defaultMessage="Mastodon is the best way to keep up with what's happening." /></strong></p>
|
||||||
|
<p><FormattedMessage id='sign_in_banner.follow_anyone' defaultMessage='Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.' /></p>
|
||||||
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
|
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -44,7 +45,8 @@ const SignInBanner = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='sign-in-banner'>
|
<div className='sign-in-banner'>
|
||||||
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
|
<p><strong><FormattedMessage id='sign_in_banner.mastodon_is' defaultMessage="Mastodon is the best way to keep up with what's happening." /></strong></p>
|
||||||
|
<p><FormattedMessage id='sign_in_banner.follow_anyone' defaultMessage='Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.' /></p>
|
||||||
{signupButton}
|
{signupButton}
|
||||||
<a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
<a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -225,7 +225,11 @@
|
||||||
"domain_pill.their_username": "مُعرّفُهم الفريد على الخادم. من الممكن العثور على مستخدمين بنفس اسم المستخدم على خوادم مختلفة.",
|
"domain_pill.their_username": "مُعرّفُهم الفريد على الخادم. من الممكن العثور على مستخدمين بنفس اسم المستخدم على خوادم مختلفة.",
|
||||||
"domain_pill.username": "اسم المستخدم",
|
"domain_pill.username": "اسم المستخدم",
|
||||||
"domain_pill.whats_in_a_handle": "ما المقصود بالمُعرِّف؟",
|
"domain_pill.whats_in_a_handle": "ما المقصود بالمُعرِّف؟",
|
||||||
|
"domain_pill.who_they_are": "بما أن المعالجات تقول من هو الشخص ومكان وجوده، يمكنك التفاعل مع الناس عبر الشبكة الاجتماعية لـ <button>ActivityPub-Power منصات</button>.",
|
||||||
|
"domain_pill.who_you_are": "لأن معالجتك تقول من أنت ومكان وجودك، يمكن الناس التفاعل معك عبر الشبكة الاجتماعية لـ <button>ActivityPub-Power منصات</button>.",
|
||||||
"domain_pill.your_handle": "عنوانك الكامل:",
|
"domain_pill.your_handle": "عنوانك الكامل:",
|
||||||
|
"domain_pill.your_server": "منزلك الرقمي، حيث تعيش جميع مشاركاتك. لا تحب هذا؟ إنقل الخوادم في أي وقت واخضر متابعينك أيضًا.",
|
||||||
|
"domain_pill.your_username": "معرفك الفريد على هذا الخادم. من الممكن العثور على مستخدمين بنفس إسم المستخدم على خوادم مختلفة.",
|
||||||
"embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
|
"embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
|
||||||
"embed.preview": "إليك ما سيبدو عليه:",
|
"embed.preview": "إليك ما سيبدو عليه:",
|
||||||
"emoji_button.activity": "الأنشطة",
|
"emoji_button.activity": "الأنشطة",
|
||||||
|
@ -262,6 +266,7 @@
|
||||||
"empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر منشورات.",
|
"empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر منشورات.",
|
||||||
"empty_column.lists": "ليس عندك أية قائمة بعد. سوف تظهر قوائمك هنا إن قمت بإنشاء واحدة.",
|
"empty_column.lists": "ليس عندك أية قائمة بعد. سوف تظهر قوائمك هنا إن قمت بإنشاء واحدة.",
|
||||||
"empty_column.mutes": "لم تقم بكتم أي مستخدم بعد.",
|
"empty_column.mutes": "لم تقم بكتم أي مستخدم بعد.",
|
||||||
|
"empty_column.notification_requests": "لا يوجد شيء هنا. عندما تتلقى إشعارات جديدة، سوف تظهر هنا وفقًا لإعداداتك.",
|
||||||
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
|
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
|
||||||
"empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
|
"empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
|
||||||
"error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.",
|
"error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.",
|
||||||
|
@ -292,6 +297,8 @@
|
||||||
"filter_modal.select_filter.subtitle": "استخدم فئة موجودة أو قم بإنشاء فئة جديدة",
|
"filter_modal.select_filter.subtitle": "استخدم فئة موجودة أو قم بإنشاء فئة جديدة",
|
||||||
"filter_modal.select_filter.title": "تصفية هذا المنشور",
|
"filter_modal.select_filter.title": "تصفية هذا المنشور",
|
||||||
"filter_modal.title.status": "تصفية منشور",
|
"filter_modal.title.status": "تصفية منشور",
|
||||||
|
"filtered_notifications_banner.mentions": "{count, plural, one {إشارة} two {إشارتين} few {# إشارات} other {# إشارة}}",
|
||||||
|
"filtered_notifications_banner.pending_requests": "إشعارات من {count, plural, zero {}=0 {لا أحد} one {شخص واحد قد تعرفه} two {شخصين قد تعرفهما} few {# أشخاص قد تعرفهم} many {# شخص قد تعرفهم} other {# شخص قد تعرفهم}}",
|
||||||
"filtered_notifications_banner.title": "الإشعارات المصفاة",
|
"filtered_notifications_banner.title": "الإشعارات المصفاة",
|
||||||
"firehose.all": "الكل",
|
"firehose.all": "الكل",
|
||||||
"firehose.local": "هذا الخادم",
|
"firehose.local": "هذا الخادم",
|
||||||
|
@ -301,6 +308,8 @@
|
||||||
"follow_requests.unlocked_explanation": "حتى وإن كان حسابك غير مقفل، يعتقد فريق {domain} أنك قد ترغب في مراجعة طلبات المتابعة من هذه الحسابات يدوياً.",
|
"follow_requests.unlocked_explanation": "حتى وإن كان حسابك غير مقفل، يعتقد فريق {domain} أنك قد ترغب في مراجعة طلبات المتابعة من هذه الحسابات يدوياً.",
|
||||||
"follow_suggestions.curated_suggestion": "اختيار الموظفين",
|
"follow_suggestions.curated_suggestion": "اختيار الموظفين",
|
||||||
"follow_suggestions.dismiss": "لا تُظهرها مجدّدًا",
|
"follow_suggestions.dismiss": "لا تُظهرها مجدّدًا",
|
||||||
|
"follow_suggestions.featured_longer": "مختار يدوياً من قِبل فريق {domain}",
|
||||||
|
"follow_suggestions.friends_of_friends_longer": "مشهور بين الأشخاص الذين تتابعهم",
|
||||||
"follow_suggestions.hints.featured": "تم اختيار هذا الملف الشخصي يدوياً من قبل فريق {domain}.",
|
"follow_suggestions.hints.featured": "تم اختيار هذا الملف الشخصي يدوياً من قبل فريق {domain}.",
|
||||||
"follow_suggestions.hints.friends_of_friends": "هذا الملف الشخصي مشهور بين الأشخاص الذين تتابعهم.",
|
"follow_suggestions.hints.friends_of_friends": "هذا الملف الشخصي مشهور بين الأشخاص الذين تتابعهم.",
|
||||||
"follow_suggestions.hints.most_followed": "هذا الملف الشخصي هو واحد من الأكثر متابعة على {domain}.",
|
"follow_suggestions.hints.most_followed": "هذا الملف الشخصي هو واحد من الأكثر متابعة على {domain}.",
|
||||||
|
@ -405,6 +414,7 @@
|
||||||
"limited_account_hint.action": "إظهار الملف التعريفي على أي حال",
|
"limited_account_hint.action": "إظهار الملف التعريفي على أي حال",
|
||||||
"limited_account_hint.title": "تم إخفاء هذا الملف الشخصي من قبل مشرفي {domain}.",
|
"limited_account_hint.title": "تم إخفاء هذا الملف الشخصي من قبل مشرفي {domain}.",
|
||||||
"link_preview.author": "مِن {name}",
|
"link_preview.author": "مِن {name}",
|
||||||
|
"link_preview.more_from_author": "المزيد من {name}",
|
||||||
"lists.account.add": "أضف إلى القائمة",
|
"lists.account.add": "أضف إلى القائمة",
|
||||||
"lists.account.remove": "احذف من القائمة",
|
"lists.account.remove": "احذف من القائمة",
|
||||||
"lists.delete": "احذف القائمة",
|
"lists.delete": "احذف القائمة",
|
||||||
|
@ -465,10 +475,13 @@
|
||||||
"notification.follow_request": "لقد طلب {name} متابعتك",
|
"notification.follow_request": "لقد طلب {name} متابعتك",
|
||||||
"notification.mention": "{name} ذكرك",
|
"notification.mention": "{name} ذكرك",
|
||||||
"notification.moderation-warning.learn_more": "اعرف المزيد",
|
"notification.moderation-warning.learn_more": "اعرف المزيد",
|
||||||
|
"notification.moderation_warning": "لقد تلقيت تحذيرًا بالإشراف",
|
||||||
|
"notification.moderation_warning.action_delete_statuses": "تم إزالة بعض مشاركاتك.",
|
||||||
"notification.moderation_warning.action_disable": "تم تعطيل حسابك.",
|
"notification.moderation_warning.action_disable": "تم تعطيل حسابك.",
|
||||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "بعض من منشوراتك تم تصنيفها على أنها حساسة.",
|
"notification.moderation_warning.action_mark_statuses_as_sensitive": "بعض من منشوراتك تم تصنيفها على أنها حساسة.",
|
||||||
"notification.moderation_warning.action_none": "لقد تلقى حسابك تحذيرا بالإشراف.",
|
"notification.moderation_warning.action_none": "لقد تلقى حسابك تحذيرا بالإشراف.",
|
||||||
"notification.moderation_warning.action_sensitive": "سيتم وضع علامة على منشوراتك على أنها حساسة من الآن فصاعدا.",
|
"notification.moderation_warning.action_sensitive": "سيتم وضع علامة على منشوراتك على أنها حساسة من الآن فصاعدا.",
|
||||||
|
"notification.moderation_warning.action_silence": "لقد تم تقييد حسابك.",
|
||||||
"notification.moderation_warning.action_suspend": "لقد تم تعليق حسابك.",
|
"notification.moderation_warning.action_suspend": "لقد تم تعليق حسابك.",
|
||||||
"notification.own_poll": "انتهى استطلاعك للرأي",
|
"notification.own_poll": "انتهى استطلاعك للرأي",
|
||||||
"notification.poll": "لقد انتهى استطلاع رأي شاركتَ فيه",
|
"notification.poll": "لقد انتهى استطلاع رأي شاركتَ فيه",
|
||||||
|
|
|
@ -308,6 +308,8 @@
|
||||||
"follow_requests.unlocked_explanation": "Ваш акаўнт не схаваны, аднак прадстаўнікі {domain} палічылі, што вы можаце захацець праглядзець запыты на падпіску з гэтых профіляў уручную.",
|
"follow_requests.unlocked_explanation": "Ваш акаўнт не схаваны, аднак прадстаўнікі {domain} палічылі, што вы можаце захацець праглядзець запыты на падпіску з гэтых профіляў уручную.",
|
||||||
"follow_suggestions.curated_suggestion": "Выбар адміністрацыі",
|
"follow_suggestions.curated_suggestion": "Выбар адміністрацыі",
|
||||||
"follow_suggestions.dismiss": "Не паказваць зноў",
|
"follow_suggestions.dismiss": "Не паказваць зноў",
|
||||||
|
"follow_suggestions.featured_longer": "Адабраныя камандай {domain} уручную",
|
||||||
|
"follow_suggestions.friends_of_friends_longer": "Папулярнае сярод людзей, на якіх Вы падпісаны",
|
||||||
"follow_suggestions.hints.featured": "Гэты профіль быў выбраны ўручную камандай {domain}.",
|
"follow_suggestions.hints.featured": "Гэты профіль быў выбраны ўручную камандай {domain}.",
|
||||||
"follow_suggestions.hints.friends_of_friends": "Гэты профіль папулярны сярод людзей, на якіх вы падпісаліся.",
|
"follow_suggestions.hints.friends_of_friends": "Гэты профіль папулярны сярод людзей, на якіх вы падпісаліся.",
|
||||||
"follow_suggestions.hints.most_followed": "Гэты профіль - адзін з профіляў з самай вялікай колькасцю падпісак на {domain}.",
|
"follow_suggestions.hints.most_followed": "Гэты профіль - адзін з профіляў з самай вялікай колькасцю падпісак на {domain}.",
|
||||||
|
@ -315,6 +317,8 @@
|
||||||
"follow_suggestions.hints.similar_to_recently_followed": "Гэты профіль падобны на профілі, на якія вы нядаўна падпісаліся.",
|
"follow_suggestions.hints.similar_to_recently_followed": "Гэты профіль падобны на профілі, на якія вы нядаўна падпісаліся.",
|
||||||
"follow_suggestions.personalized_suggestion": "Персаналізаваная прапанова",
|
"follow_suggestions.personalized_suggestion": "Персаналізаваная прапанова",
|
||||||
"follow_suggestions.popular_suggestion": "Папулярная прапанова",
|
"follow_suggestions.popular_suggestion": "Папулярная прапанова",
|
||||||
|
"follow_suggestions.popular_suggestion_longer": "Папулярнае на {domain}",
|
||||||
|
"follow_suggestions.similar_to_recently_followed_longer": "Падобныя профілі, за якімі вы нядаўна сачылі",
|
||||||
"follow_suggestions.view_all": "Праглядзець усё",
|
"follow_suggestions.view_all": "Праглядзець усё",
|
||||||
"follow_suggestions.who_to_follow": "На каго падпісацца",
|
"follow_suggestions.who_to_follow": "На каго падпісацца",
|
||||||
"followed_tags": "Падпіскі",
|
"followed_tags": "Падпіскі",
|
||||||
|
@ -410,6 +414,7 @@
|
||||||
"limited_account_hint.action": "Усе роўна паказваць профіль",
|
"limited_account_hint.action": "Усе роўна паказваць профіль",
|
||||||
"limited_account_hint.title": "Гэты профіль быў схаваны мадэратарамі",
|
"limited_account_hint.title": "Гэты профіль быў схаваны мадэратарамі",
|
||||||
"link_preview.author": "Ад {name}",
|
"link_preview.author": "Ад {name}",
|
||||||
|
"link_preview.more_from_author": "Больш ад {name}",
|
||||||
"lists.account.add": "Дадаць да спісу",
|
"lists.account.add": "Дадаць да спісу",
|
||||||
"lists.account.remove": "Выдаліць са спісу",
|
"lists.account.remove": "Выдаліць са спісу",
|
||||||
"lists.delete": "Выдаліць спіс",
|
"lists.delete": "Выдаліць спіс",
|
||||||
|
@ -458,7 +463,7 @@
|
||||||
"navigation_bar.opened_in_classic_interface": "Допісы, уліковыя запісы і іншыя спецыфічныя старонкі па змоўчанні адчыняюцца ў класічным вэб-інтэрфейсе.",
|
"navigation_bar.opened_in_classic_interface": "Допісы, уліковыя запісы і іншыя спецыфічныя старонкі па змоўчанні адчыняюцца ў класічным вэб-інтэрфейсе.",
|
||||||
"navigation_bar.personal": "Асабістае",
|
"navigation_bar.personal": "Асабістае",
|
||||||
"navigation_bar.pins": "Замацаваныя допісы",
|
"navigation_bar.pins": "Замацаваныя допісы",
|
||||||
"navigation_bar.preferences": "Параметры",
|
"navigation_bar.preferences": "Налады",
|
||||||
"navigation_bar.public_timeline": "Глабальная стужка",
|
"navigation_bar.public_timeline": "Глабальная стужка",
|
||||||
"navigation_bar.search": "Пошук",
|
"navigation_bar.search": "Пошук",
|
||||||
"navigation_bar.security": "Бяспека",
|
"navigation_bar.security": "Бяспека",
|
||||||
|
@ -470,10 +475,22 @@
|
||||||
"notification.follow_request": "{name} адправіў запыт на падпіску",
|
"notification.follow_request": "{name} адправіў запыт на падпіску",
|
||||||
"notification.mention": "{name} згадаў вас",
|
"notification.mention": "{name} згадаў вас",
|
||||||
"notification.moderation-warning.learn_more": "Даведацца больш",
|
"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.own_poll": "Ваша апытанне скончылася",
|
||||||
"notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
|
"notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
|
||||||
"notification.reblog": "{name} пашырыў ваш допіс",
|
"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.learn_more": "Даведацца больш",
|
||||||
|
"notification.relationships_severance_event.user_domain_block": "Вы заблакіравалі {target} выдаліўшы {followersCount} сваіх падпісчыкаў і {followingCount, plural, one {# уліковы запіс} few {# уліковыя запісы} many {# уліковых запісаў} other {# уліковых запісаў}}, за якімі вы сочыце.",
|
||||||
"notification.status": "Новы допіс ад {name}",
|
"notification.status": "Новы допіс ад {name}",
|
||||||
"notification.update": "Допіс {name} адрэдагаваны",
|
"notification.update": "Допіс {name} адрэдагаваны",
|
||||||
"notification_requests.accept": "Прыняць",
|
"notification_requests.accept": "Прыняць",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Aquest perfil l'han amagat els moderadors de {domain}.",
|
"limited_account_hint.title": "Aquest perfil l'han amagat els moderadors de {domain}.",
|
||||||
"link_preview.author": "Per {name}",
|
"link_preview.author": "Per {name}",
|
||||||
"link_preview.more_from_author": "Més de {name}",
|
"link_preview.more_from_author": "Més de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} publicació} other {{counter} publicacions}}",
|
||||||
"lists.account.add": "Afegeix a la llista",
|
"lists.account.add": "Afegeix a la llista",
|
||||||
"lists.account.remove": "Elimina de la llista",
|
"lists.account.remove": "Elimina de la llista",
|
||||||
"lists.delete": "Elimina la llista",
|
"lists.delete": "Elimina la llista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Denne profil er blevet skjult af {domain}-moderatorerne.",
|
"limited_account_hint.title": "Denne profil er blevet skjult af {domain}-moderatorerne.",
|
||||||
"link_preview.author": "Af {name}",
|
"link_preview.author": "Af {name}",
|
||||||
"link_preview.more_from_author": "Mere fra {name}",
|
"link_preview.more_from_author": "Mere fra {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}}",
|
||||||
"lists.account.add": "Føj til liste",
|
"lists.account.add": "Føj til liste",
|
||||||
"lists.account.remove": "Fjern fra liste",
|
"lists.account.remove": "Fjern fra liste",
|
||||||
"lists.delete": "Slet liste",
|
"lists.delete": "Slet liste",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.",
|
"limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.",
|
||||||
"link_preview.author": "Von {name}",
|
"link_preview.author": "Von {name}",
|
||||||
"link_preview.more_from_author": "Mehr von {name}",
|
"link_preview.more_from_author": "Mehr von {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}",
|
||||||
"lists.account.add": "Zur Liste hinzufügen",
|
"lists.account.add": "Zur Liste hinzufügen",
|
||||||
"lists.account.remove": "Von der Liste entfernen",
|
"lists.account.remove": "Von der Liste entfernen",
|
||||||
"lists.delete": "Liste löschen",
|
"lists.delete": "Liste löschen",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
||||||
"link_preview.author": "By {name}",
|
"link_preview.author": "By {name}",
|
||||||
"link_preview.more_from_author": "More from {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.add": "Add to list",
|
||||||
"lists.account.remove": "Remove from list",
|
"lists.account.remove": "Remove from list",
|
||||||
"lists.delete": "Delete list",
|
"lists.delete": "Delete list",
|
||||||
|
@ -695,13 +696,13 @@
|
||||||
"server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
|
"server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
|
||||||
"server_banner.active_users": "active users",
|
"server_banner.active_users": "active users",
|
||||||
"server_banner.administered_by": "Administered by:",
|
"server_banner.administered_by": "Administered by:",
|
||||||
"server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
|
"server_banner.is_one_of_many": "{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.",
|
||||||
"server_banner.learn_more": "Learn more",
|
|
||||||
"server_banner.server_stats": "Server stats:",
|
"server_banner.server_stats": "Server stats:",
|
||||||
"sign_in_banner.create_account": "Create account",
|
"sign_in_banner.create_account": "Create account",
|
||||||
|
"sign_in_banner.follow_anyone": "Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.",
|
||||||
|
"sign_in_banner.mastodon_is": "Mastodon is the best way to keep up with what's happening.",
|
||||||
"sign_in_banner.sign_in": "Login",
|
"sign_in_banner.sign_in": "Login",
|
||||||
"sign_in_banner.sso_redirect": "Login or Register",
|
"sign_in_banner.sso_redirect": "Login or Register",
|
||||||
"sign_in_banner.text": "Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.",
|
|
||||||
"status.admin_account": "Open moderation interface for @{name}",
|
"status.admin_account": "Open moderation interface for @{name}",
|
||||||
"status.admin_domain": "Open moderation interface for {domain}",
|
"status.admin_domain": "Open moderation interface for {domain}",
|
||||||
"status.admin_status": "Open this post in the moderation interface",
|
"status.admin_status": "Open this post in the moderation interface",
|
||||||
|
|
|
@ -498,7 +498,14 @@
|
||||||
"poll_button.add_poll": "Aldoni balotenketon",
|
"poll_button.add_poll": "Aldoni balotenketon",
|
||||||
"poll_button.remove_poll": "Forigi balotenketon",
|
"poll_button.remove_poll": "Forigi balotenketon",
|
||||||
"privacy.change": "Agordi mesaĝan privatecon",
|
"privacy.change": "Agordi mesaĝan privatecon",
|
||||||
|
"privacy.direct.long": "Ĉiuj menciitaj en la afiŝo",
|
||||||
|
"privacy.direct.short": "Specifaj homoj",
|
||||||
|
"privacy.private.long": "Nur viaj sekvantoj",
|
||||||
|
"privacy.private.short": "Sekvantoj",
|
||||||
|
"privacy.public.long": "Ĉiujn ajn ĉe kaj ekster Mastodon",
|
||||||
"privacy.public.short": "Publika",
|
"privacy.public.short": "Publika",
|
||||||
|
"privacy.unlisted.long": "Malpli algoritmaj fanfaroj",
|
||||||
|
"privacy.unlisted.short": "Diskrete publika",
|
||||||
"privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
|
"privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
|
||||||
"privacy_policy.title": "Politiko de privateco",
|
"privacy_policy.title": "Politiko de privateco",
|
||||||
"recommended": "Rekomendita",
|
"recommended": "Rekomendita",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Este perfil fue ocultado por los moderadores de {domain}.",
|
"limited_account_hint.title": "Este perfil fue ocultado por los moderadores de {domain}.",
|
||||||
"link_preview.author": "Por {name}",
|
"link_preview.author": "Por {name}",
|
||||||
"link_preview.more_from_author": "Más de {name}",
|
"link_preview.more_from_author": "Más de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} mensaje} other {{counter} mensajes}}",
|
||||||
"lists.account.add": "Agregar a lista",
|
"lists.account.add": "Agregar a lista",
|
||||||
"lists.account.remove": "Quitar de lista",
|
"lists.account.remove": "Quitar de lista",
|
||||||
"lists.delete": "Eliminar lista",
|
"lists.delete": "Eliminar lista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
|
"limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
|
||||||
"link_preview.author": "Por {name}",
|
"link_preview.author": "Por {name}",
|
||||||
"link_preview.more_from_author": "Más de {name}",
|
"link_preview.more_from_author": "Más de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
|
||||||
"lists.account.add": "Añadir a lista",
|
"lists.account.add": "Añadir a lista",
|
||||||
"lists.account.remove": "Quitar de lista",
|
"lists.account.remove": "Quitar de lista",
|
||||||
"lists.delete": "Borrar lista",
|
"lists.delete": "Borrar lista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
|
"limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
|
||||||
"link_preview.author": "Por {name}",
|
"link_preview.author": "Por {name}",
|
||||||
"link_preview.more_from_author": "Más de {name}",
|
"link_preview.more_from_author": "Más de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
|
||||||
"lists.account.add": "Añadir a lista",
|
"lists.account.add": "Añadir a lista",
|
||||||
"lists.account.remove": "Quitar de lista",
|
"lists.account.remove": "Quitar de lista",
|
||||||
"lists.delete": "Borrar lista",
|
"lists.delete": "Borrar lista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Palvelimen {domain} valvojat ovat piilottaneet tämän käyttäjätilin.",
|
"limited_account_hint.title": "Palvelimen {domain} valvojat ovat piilottaneet tämän käyttäjätilin.",
|
||||||
"link_preview.author": "Julkaissut {name}",
|
"link_preview.author": "Julkaissut {name}",
|
||||||
"link_preview.more_from_author": "Lisää käyttäjältä {name}",
|
"link_preview.more_from_author": "Lisää käyttäjältä {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}",
|
||||||
"lists.account.add": "Lisää listalle",
|
"lists.account.add": "Lisää listalle",
|
||||||
"lists.account.remove": "Poista listalta",
|
"lists.account.remove": "Poista listalta",
|
||||||
"lists.delete": "Poista lista",
|
"lists.delete": "Poista lista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Hesin vangin er fjaldur av kjakleiðarunum á {domain}.",
|
"limited_account_hint.title": "Hesin vangin er fjaldur av kjakleiðarunum á {domain}.",
|
||||||
"link_preview.author": "Av {name}",
|
"link_preview.author": "Av {name}",
|
||||||
"link_preview.more_from_author": "Meira frá {name}",
|
"link_preview.more_from_author": "Meira frá {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} postur} other {{counter} postar}}",
|
||||||
"lists.account.add": "Legg afturat lista",
|
"lists.account.add": "Legg afturat lista",
|
||||||
"lists.account.remove": "Tak av lista",
|
"lists.account.remove": "Tak av lista",
|
||||||
"lists.delete": "Strika lista",
|
"lists.delete": "Strika lista",
|
||||||
|
|
|
@ -156,7 +156,7 @@
|
||||||
"compose_form.poll.duration": "Durée du sondage",
|
"compose_form.poll.duration": "Durée du sondage",
|
||||||
"compose_form.poll.multiple": "Choix multiple",
|
"compose_form.poll.multiple": "Choix multiple",
|
||||||
"compose_form.poll.option_placeholder": "Option {number}",
|
"compose_form.poll.option_placeholder": "Option {number}",
|
||||||
"compose_form.poll.single": "Choisissez-en un",
|
"compose_form.poll.single": "Choix unique",
|
||||||
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
||||||
"compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix",
|
"compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix",
|
||||||
"compose_form.poll.type": "Style",
|
"compose_form.poll.type": "Style",
|
||||||
|
@ -585,9 +585,9 @@
|
||||||
"privacy.private.short": "Abonnés",
|
"privacy.private.short": "Abonnés",
|
||||||
"privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
|
"privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
|
||||||
"privacy.public.short": "Public",
|
"privacy.public.short": "Public",
|
||||||
"privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.",
|
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
||||||
"privacy.unlisted.long": "Moins de fanfares algorithmiques",
|
"privacy.unlisted.long": "Moins de fanfares algorithmiques",
|
||||||
"privacy.unlisted.short": "Public calme",
|
"privacy.unlisted.short": "Public discret",
|
||||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
"recommended": "Recommandé",
|
"recommended": "Recommandé",
|
||||||
|
|
|
@ -156,7 +156,7 @@
|
||||||
"compose_form.poll.duration": "Durée du sondage",
|
"compose_form.poll.duration": "Durée du sondage",
|
||||||
"compose_form.poll.multiple": "Choix multiple",
|
"compose_form.poll.multiple": "Choix multiple",
|
||||||
"compose_form.poll.option_placeholder": "Option {number}",
|
"compose_form.poll.option_placeholder": "Option {number}",
|
||||||
"compose_form.poll.single": "Choisissez-en un",
|
"compose_form.poll.single": "Choix unique",
|
||||||
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
|
||||||
"compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
|
"compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
|
||||||
"compose_form.poll.type": "Style",
|
"compose_form.poll.type": "Style",
|
||||||
|
@ -585,9 +585,9 @@
|
||||||
"privacy.private.short": "Abonnés",
|
"privacy.private.short": "Abonnés",
|
||||||
"privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
|
"privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
|
||||||
"privacy.public.short": "Public",
|
"privacy.public.short": "Public",
|
||||||
"privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.",
|
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
||||||
"privacy.unlisted.long": "Moins de fanfares algorithmiques",
|
"privacy.unlisted.long": "Moins de fanfares algorithmiques",
|
||||||
"privacy.unlisted.short": "Public calme",
|
"privacy.unlisted.short": "Public discret",
|
||||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
"recommended": "Recommandé",
|
"recommended": "Recommandé",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Este perfil foi agochado pola moderación de {domain}.",
|
"limited_account_hint.title": "Este perfil foi agochado pola moderación de {domain}.",
|
||||||
"link_preview.author": "Por {name}",
|
"link_preview.author": "Por {name}",
|
||||||
"link_preview.more_from_author": "Máis de {name}",
|
"link_preview.more_from_author": "Máis de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicacións}}",
|
||||||
"lists.account.add": "Engadir á listaxe",
|
"lists.account.add": "Engadir á listaxe",
|
||||||
"lists.account.remove": "Eliminar da listaxe",
|
"lists.account.remove": "Eliminar da listaxe",
|
||||||
"lists.delete": "Eliminar listaxe",
|
"lists.delete": "Eliminar listaxe",
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"account.block_domain": "Blocar dominio {domain}",
|
"account.block_domain": "Blocar dominio {domain}",
|
||||||
"account.block_short": "Blocar",
|
"account.block_short": "Blocar",
|
||||||
"account.blocked": "Blocate",
|
"account.blocked": "Blocate",
|
||||||
"account.browse_more_on_origin_server": "Percurrer plus sur le profilo original",
|
"account.browse_more_on_origin_server": "Explorar plus sur le profilo original",
|
||||||
"account.cancel_follow_request": "Cancellar sequimento",
|
"account.cancel_follow_request": "Cancellar sequimento",
|
||||||
"account.copy": "Copiar ligamine a profilo",
|
"account.copy": "Copiar ligamine a profilo",
|
||||||
"account.direct": "Mentionar privatemente @{name}",
|
"account.direct": "Mentionar privatemente @{name}",
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"account.open_original_page": "Aperir le pagina original",
|
"account.open_original_page": "Aperir le pagina original",
|
||||||
"account.posts": "Messages",
|
"account.posts": "Messages",
|
||||||
"account.posts_with_replies": "Messages e responsas",
|
"account.posts_with_replies": "Messages e responsas",
|
||||||
"account.report": "Signalar @{name}",
|
"account.report": "Reportar @{name}",
|
||||||
"account.requested": "Attendente le approbation. Clicca pro cancellar le requesta de sequer",
|
"account.requested": "Attendente le approbation. Clicca pro cancellar le requesta de sequer",
|
||||||
"account.requested_follow": "{name} ha requestate de sequer te",
|
"account.requested_follow": "{name} ha requestate de sequer te",
|
||||||
"account.share": "Compartir profilo de @{name}",
|
"account.share": "Compartir profilo de @{name}",
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
"bundle_modal_error.message": "Un error ha occurrite durante le cargamento de iste componente.",
|
"bundle_modal_error.message": "Un error ha occurrite durante le cargamento de iste componente.",
|
||||||
"bundle_modal_error.retry": "Tentar novemente",
|
"bundle_modal_error.retry": "Tentar novemente",
|
||||||
"closed_registrations.other_server_instructions": "Perque Mastodon es decentralisate, tu pote crear un conto sur un altere servitor e totevia interager con iste servitor.",
|
"closed_registrations.other_server_instructions": "Perque Mastodon es decentralisate, tu pote crear un conto sur un altere servitor e totevia interager con iste servitor.",
|
||||||
"closed_registrations_modal.description": "Crear un conto in {domain} actualmente non es possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.",
|
"closed_registrations_modal.description": "Crear un conto sur {domain} non es actualmente possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.",
|
||||||
"closed_registrations_modal.find_another_server": "Cercar un altere servitor",
|
"closed_registrations_modal.find_another_server": "Cercar un altere servitor",
|
||||||
"closed_registrations_modal.preamble": "Mastodon es decentralisate, dunque, non importa ubi tu crea tu conto, tu pote sequer e communicar con omne persona sur iste servitor. Tu pote mesmo hospitar tu proprie servitor!",
|
"closed_registrations_modal.preamble": "Mastodon es decentralisate, dunque, non importa ubi tu crea tu conto, tu pote sequer e communicar con omne persona sur iste servitor. Tu pote mesmo hospitar tu proprie servitor!",
|
||||||
"closed_registrations_modal.title": "Crear un conto sur Mastodon",
|
"closed_registrations_modal.title": "Crear un conto sur Mastodon",
|
||||||
|
@ -274,7 +274,7 @@
|
||||||
"error.unexpected_crash.next_steps": "Tenta refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.",
|
"error.unexpected_crash.next_steps": "Tenta refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.",
|
||||||
"error.unexpected_crash.next_steps_addons": "Tenta disactivar istes e refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.",
|
"error.unexpected_crash.next_steps_addons": "Tenta disactivar istes e refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.",
|
||||||
"errors.unexpected_crash.copy_stacktrace": "Copiar le traciamento del pila al area de transferentia",
|
"errors.unexpected_crash.copy_stacktrace": "Copiar le traciamento del pila al area de transferentia",
|
||||||
"errors.unexpected_crash.report_issue": "Signalar un defecto",
|
"errors.unexpected_crash.report_issue": "Reportar problema",
|
||||||
"explore.search_results": "Resultatos de recerca",
|
"explore.search_results": "Resultatos de recerca",
|
||||||
"explore.suggested_follows": "Personas",
|
"explore.suggested_follows": "Personas",
|
||||||
"explore.title": "Explorar",
|
"explore.title": "Explorar",
|
||||||
|
@ -468,7 +468,7 @@
|
||||||
"navigation_bar.search": "Cercar",
|
"navigation_bar.search": "Cercar",
|
||||||
"navigation_bar.security": "Securitate",
|
"navigation_bar.security": "Securitate",
|
||||||
"not_signed_in_indicator.not_signed_in": "Es necessari aperir session pro acceder a iste ressource.",
|
"not_signed_in_indicator.not_signed_in": "Es necessari aperir session pro acceder a iste ressource.",
|
||||||
"notification.admin.report": "{name} ha signalate {target}",
|
"notification.admin.report": "{name} ha reportate {target}",
|
||||||
"notification.admin.sign_up": "{name} se ha inscribite",
|
"notification.admin.sign_up": "{name} se ha inscribite",
|
||||||
"notification.favourite": "{name} ha marcate tu message como favorite",
|
"notification.favourite": "{name} ha marcate tu message como favorite",
|
||||||
"notification.follow": "{name} te ha sequite",
|
"notification.follow": "{name} te ha sequite",
|
||||||
|
@ -499,7 +499,7 @@
|
||||||
"notification_requests.title": "Notificationes filtrate",
|
"notification_requests.title": "Notificationes filtrate",
|
||||||
"notifications.clear": "Rader notificationes",
|
"notifications.clear": "Rader notificationes",
|
||||||
"notifications.clear_confirmation": "Es tu secur que tu vole rader permanentemente tote tu notificationes?",
|
"notifications.clear_confirmation": "Es tu secur que tu vole rader permanentemente tote tu notificationes?",
|
||||||
"notifications.column_settings.admin.report": "Nove signalationes:",
|
"notifications.column_settings.admin.report": "Nove reportos:",
|
||||||
"notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
|
"notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
|
||||||
"notifications.column_settings.alert": "Notificationes de scriptorio",
|
"notifications.column_settings.alert": "Notificationes de scriptorio",
|
||||||
"notifications.column_settings.favourite": "Favorites:",
|
"notifications.column_settings.favourite": "Favorites:",
|
||||||
|
@ -636,7 +636,7 @@
|
||||||
"report.close": "Facite",
|
"report.close": "Facite",
|
||||||
"report.comment.title": "Ha il altere cosas que nos deberea saper?",
|
"report.comment.title": "Ha il altere cosas que nos deberea saper?",
|
||||||
"report.forward": "Reinviar a {target}",
|
"report.forward": "Reinviar a {target}",
|
||||||
"report.forward_hint": "Le conto es de un altere servitor. Inviar un copia anonymisate del signalation a illo tamben?",
|
"report.forward_hint": "Le conto es de un altere servitor. Inviar un copia anonymisate del reporto a illo tamben?",
|
||||||
"report.mute": "Silentiar",
|
"report.mute": "Silentiar",
|
||||||
"report.mute_explanation": "Tu non videra le messages de iste persona. Ille pote totevia sequer te e vider tu messages e non sapera de esser silentiate.",
|
"report.mute_explanation": "Tu non videra le messages de iste persona. Ille pote totevia sequer te e vider tu messages e non sapera de esser silentiate.",
|
||||||
"report.next": "Sequente",
|
"report.next": "Sequente",
|
||||||
|
@ -656,11 +656,11 @@
|
||||||
"report.statuses.subtitle": "Selige tote le responsas appropriate",
|
"report.statuses.subtitle": "Selige tote le responsas appropriate",
|
||||||
"report.statuses.title": "Existe alcun messages que appoia iste reporto?",
|
"report.statuses.title": "Existe alcun messages que appoia iste reporto?",
|
||||||
"report.submit": "Submitter",
|
"report.submit": "Submitter",
|
||||||
"report.target": "Signalamento de {target}",
|
"report.target": "Reportage de {target}",
|
||||||
"report.thanks.take_action": "Ecce tu optiones pro controlar lo que tu vide sur Mastodon:",
|
"report.thanks.take_action": "Ecce tu optiones pro controlar lo que tu vide sur Mastodon:",
|
||||||
"report.thanks.take_action_actionable": "Durante que nos revide isto, tu pote prender mesuras contra @{name}:",
|
"report.thanks.take_action_actionable": "Durante que nos revide isto, tu pote prender mesuras contra @{name}:",
|
||||||
"report.thanks.title": "Non vole vider isto?",
|
"report.thanks.title": "Non vole vider isto?",
|
||||||
"report.thanks.title_actionable": "Gratias pro signalar, nos investigara isto.",
|
"report.thanks.title_actionable": "Gratias pro reportar, nos investigara isto.",
|
||||||
"report.unfollow": "Cessar de sequer @{name}",
|
"report.unfollow": "Cessar de sequer @{name}",
|
||||||
"report.unfollow_explanation": "Tu seque iste conto. Pro non plus vider su messages in tu fluxo de initio, cessa de sequer lo.",
|
"report.unfollow_explanation": "Tu seque iste conto. Pro non plus vider su messages in tu fluxo de initio, cessa de sequer lo.",
|
||||||
"report_notification.attached_statuses": "{count, plural, one {{count} message} other {{count} messages}} annexate",
|
"report_notification.attached_statuses": "{count, plural, one {{count} message} other {{count} messages}} annexate",
|
||||||
|
@ -747,7 +747,7 @@
|
||||||
"status.replied_to": "Respondite a {name}",
|
"status.replied_to": "Respondite a {name}",
|
||||||
"status.reply": "Responder",
|
"status.reply": "Responder",
|
||||||
"status.replyAll": "Responder al discussion",
|
"status.replyAll": "Responder al discussion",
|
||||||
"status.report": "Signalar @{name}",
|
"status.report": "Reportar @{name}",
|
||||||
"status.sensitive_warning": "Contento sensibile",
|
"status.sensitive_warning": "Contento sensibile",
|
||||||
"status.share": "Compartir",
|
"status.share": "Compartir",
|
||||||
"status.show_filter_reason": "Monstrar in omne caso",
|
"status.show_filter_reason": "Monstrar in omne caso",
|
||||||
|
|
|
@ -299,6 +299,11 @@
|
||||||
"follow_suggestions.dismiss": "Jangan tampilkan lagi",
|
"follow_suggestions.dismiss": "Jangan tampilkan lagi",
|
||||||
"follow_suggestions.hints.featured": "Profil ini telah dipilih sendiri oleh tim {domain}.",
|
"follow_suggestions.hints.featured": "Profil ini telah dipilih sendiri oleh tim {domain}.",
|
||||||
"follow_suggestions.hints.friends_of_friends": "Profil ini populer di kalangan orang yang anda ikuti.",
|
"follow_suggestions.hints.friends_of_friends": "Profil ini populer di kalangan orang yang anda ikuti.",
|
||||||
|
"follow_suggestions.personalized_suggestion": "Saran yang dipersonalisasi",
|
||||||
|
"follow_suggestions.popular_suggestion": "Saran populer",
|
||||||
|
"follow_suggestions.popular_suggestion_longer": "Populer di {domain}",
|
||||||
|
"follow_suggestions.similar_to_recently_followed_longer": "Serupa dengan profil yang baru Anda ikuti",
|
||||||
|
"follow_suggestions.view_all": "Lihat semua",
|
||||||
"followed_tags": "Tagar yang diikuti",
|
"followed_tags": "Tagar yang diikuti",
|
||||||
"footer.about": "Tentang",
|
"footer.about": "Tentang",
|
||||||
"footer.directory": "Direktori profil",
|
"footer.directory": "Direktori profil",
|
||||||
|
@ -324,6 +329,7 @@
|
||||||
"home.column_settings.show_reblogs": "Tampilkan boost",
|
"home.column_settings.show_reblogs": "Tampilkan boost",
|
||||||
"home.column_settings.show_replies": "Tampilkan balasan",
|
"home.column_settings.show_replies": "Tampilkan balasan",
|
||||||
"home.hide_announcements": "Sembunyikan pengumuman",
|
"home.hide_announcements": "Sembunyikan pengumuman",
|
||||||
|
"home.pending_critical_update.link": "Lihat pembaruan",
|
||||||
"home.show_announcements": "Tampilkan pengumuman",
|
"home.show_announcements": "Tampilkan pengumuman",
|
||||||
"interaction_modal.description.follow": "Dengan sebuah akun di Mastodon, Anda bisa mengikuti {name} untuk menerima kirimannya di beranda Anda.",
|
"interaction_modal.description.follow": "Dengan sebuah akun di Mastodon, Anda bisa mengikuti {name} untuk menerima kirimannya di beranda Anda.",
|
||||||
"interaction_modal.description.reblog": "Dengan sebuah akun di Mastodon, Anda bisa mem-boost kiriman ini untuk membagikannya ke pengikut Anda sendiri.",
|
"interaction_modal.description.reblog": "Dengan sebuah akun di Mastodon, Anda bisa mem-boost kiriman ini untuk membagikannya ke pengikut Anda sendiri.",
|
||||||
|
@ -375,6 +381,7 @@
|
||||||
"lightbox.previous": "Sebelumnya",
|
"lightbox.previous": "Sebelumnya",
|
||||||
"limited_account_hint.action": "Tetap tampilkan profil",
|
"limited_account_hint.action": "Tetap tampilkan profil",
|
||||||
"limited_account_hint.title": "Profil ini telah disembunyikan oleh moderator {domain}.",
|
"limited_account_hint.title": "Profil ini telah disembunyikan oleh moderator {domain}.",
|
||||||
|
"link_preview.author": "Oleh {name}",
|
||||||
"lists.account.add": "Tambah ke daftar",
|
"lists.account.add": "Tambah ke daftar",
|
||||||
"lists.account.remove": "Hapus dari daftar",
|
"lists.account.remove": "Hapus dari daftar",
|
||||||
"lists.delete": "Hapus daftar",
|
"lists.delete": "Hapus daftar",
|
||||||
|
@ -389,8 +396,11 @@
|
||||||
"lists.search": "Cari di antara orang yang Anda ikuti",
|
"lists.search": "Cari di antara orang yang Anda ikuti",
|
||||||
"lists.subheading": "Daftar Anda",
|
"lists.subheading": "Daftar Anda",
|
||||||
"load_pending": "{count, plural, other {# item baru}}",
|
"load_pending": "{count, plural, other {# item baru}}",
|
||||||
|
"loading_indicator.label": "Memuat…",
|
||||||
"media_gallery.toggle_visible": "Tampil/Sembunyikan",
|
"media_gallery.toggle_visible": "Tampil/Sembunyikan",
|
||||||
"moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.",
|
"moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.",
|
||||||
|
"mute_modal.hide_options": "Sembunyikan opsi",
|
||||||
|
"mute_modal.title": "Bisukan pengguna?",
|
||||||
"navigation_bar.about": "Tentang",
|
"navigation_bar.about": "Tentang",
|
||||||
"navigation_bar.blocks": "Pengguna diblokir",
|
"navigation_bar.blocks": "Pengguna diblokir",
|
||||||
"navigation_bar.bookmarks": "Markah",
|
"navigation_bar.bookmarks": "Markah",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Þetta notandasnið hefur verið falið af umsjónarmönnum {domain}.",
|
"limited_account_hint.title": "Þetta notandasnið hefur verið falið af umsjónarmönnum {domain}.",
|
||||||
"link_preview.author": "Eftir {name}",
|
"link_preview.author": "Eftir {name}",
|
||||||
"link_preview.more_from_author": "Meira frá {name}",
|
"link_preview.more_from_author": "Meira frá {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} færsla} other {{counter} færslur}}",
|
||||||
"lists.account.add": "Bæta á lista",
|
"lists.account.add": "Bæta á lista",
|
||||||
"lists.account.remove": "Fjarlægja af lista",
|
"lists.account.remove": "Fjarlægja af lista",
|
||||||
"lists.delete": "Eyða lista",
|
"lists.delete": "Eyða lista",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Questo profilo è stato nascosto dai moderatori di {domain}.",
|
"limited_account_hint.title": "Questo profilo è stato nascosto dai moderatori di {domain}.",
|
||||||
"link_preview.author": "Di {name}",
|
"link_preview.author": "Di {name}",
|
||||||
"link_preview.more_from_author": "Altro da {name}",
|
"link_preview.more_from_author": "Altro da {name}",
|
||||||
|
"link_preview.shares": "{count, plural,one {{counter} post}other {{counter} post}}",
|
||||||
"lists.account.add": "Aggiungi all'elenco",
|
"lists.account.add": "Aggiungi all'elenco",
|
||||||
"lists.account.remove": "Rimuovi dall'elenco",
|
"lists.account.remove": "Rimuovi dall'elenco",
|
||||||
"lists.delete": "Elimina elenco",
|
"lists.delete": "Elimina elenco",
|
||||||
|
|
|
@ -414,7 +414,7 @@
|
||||||
"limited_account_hint.action": "그래도 프로필 보기",
|
"limited_account_hint.action": "그래도 프로필 보기",
|
||||||
"limited_account_hint.title": "이 프로필은 {domain}의 중재자에 의해 숨겨진 상태입니다.",
|
"limited_account_hint.title": "이 프로필은 {domain}의 중재자에 의해 숨겨진 상태입니다.",
|
||||||
"link_preview.author": "{name}",
|
"link_preview.author": "{name}",
|
||||||
"link_preview.more_from_author": "{name} 더 둘러보기",
|
"link_preview.more_from_author": "{name} 프로필 보기",
|
||||||
"lists.account.add": "리스트에 추가",
|
"lists.account.add": "리스트에 추가",
|
||||||
"lists.account.remove": "리스트에서 제거",
|
"lists.account.remove": "리스트에서 제거",
|
||||||
"lists.delete": "리스트 삭제",
|
"lists.delete": "리스트 삭제",
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
{
|
{
|
||||||
"about.contact": "Ratio:",
|
"about.contact": "Ratio:",
|
||||||
"about.domain_blocks.no_reason_available": "Ratio abdere est",
|
"about.domain_blocks.no_reason_available": "Ratio abdere est",
|
||||||
|
"about.domain_blocks.silenced.explanation": "Tua profilia atque tuum contentum ab hac serve praecipue non videbis, nisi explōrēs expresse aut subsequeris et optēs.",
|
||||||
"account.account_note_header": "Annotatio",
|
"account.account_note_header": "Annotatio",
|
||||||
|
"account.add_or_remove_from_list": "Adde aut ēripe ex tabellīs",
|
||||||
"account.badges.bot": "Robotum",
|
"account.badges.bot": "Robotum",
|
||||||
"account.badges.group": "Congregatio",
|
"account.badges.group": "Congregatio",
|
||||||
"account.block": "Impedire @{name}",
|
"account.block": "Impedire @{name}",
|
||||||
|
@ -11,11 +13,21 @@
|
||||||
"account.domain_blocked": "Dominium impeditum",
|
"account.domain_blocked": "Dominium impeditum",
|
||||||
"account.edit_profile": "Recolere notionem",
|
"account.edit_profile": "Recolere notionem",
|
||||||
"account.featured_tags.last_status_never": "Nulla contributa",
|
"account.featured_tags.last_status_never": "Nulla contributa",
|
||||||
|
"account.featured_tags.title": "Hashtag notātī {name}",
|
||||||
|
"account.followers_counter": "{count, plural, one {{counter} Sectator} other {{counter} Sectatores}}",
|
||||||
|
"account.following_counter": "{count, plural, one {{counter} Sequens} other {{counter} Sequentes}}",
|
||||||
|
"account.moved_to": "{name} significavit eum suam rationem novam nunc esse:",
|
||||||
"account.muted": "Confutatus",
|
"account.muted": "Confutatus",
|
||||||
|
"account.requested_follow": "{name} postulavit ut te sequeretur",
|
||||||
|
"account.statuses_counter": "{count, plural, one {{counter} Nuntius} other {{counter} Nuntii}}",
|
||||||
"account.unblock_short": "Solvere impedimentum",
|
"account.unblock_short": "Solvere impedimentum",
|
||||||
"account_note.placeholder": "Click to add a note",
|
"account_note.placeholder": "Click to add a note",
|
||||||
"admin.dashboard.retention.average": "Mediocritas",
|
"admin.dashboard.retention.average": "Mediocritas",
|
||||||
|
"admin.impact_report.instance_accounts": "Rationes perfiles hoc deleret",
|
||||||
|
"alert.unexpected.message": "Error inopinatus occurrit.",
|
||||||
"announcement.announcement": "Proclamatio",
|
"announcement.announcement": "Proclamatio",
|
||||||
|
"attachments_list.unprocessed": "(immūtātus)",
|
||||||
|
"block_modal.you_wont_see_mentions": "Nuntios quibus eos commemorant non videbis.",
|
||||||
"bundle_column_error.error.title": "Eheu!",
|
"bundle_column_error.error.title": "Eheu!",
|
||||||
"bundle_column_error.retry": "Retemptare",
|
"bundle_column_error.retry": "Retemptare",
|
||||||
"bundle_column_error.routing.title": "CCCCIIII",
|
"bundle_column_error.routing.title": "CCCCIIII",
|
||||||
|
@ -32,30 +44,60 @@
|
||||||
"compose_form.direct_message_warning_learn_more": "Discere plura",
|
"compose_form.direct_message_warning_learn_more": "Discere plura",
|
||||||
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
|
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
|
||||||
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
|
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
|
||||||
|
"compose_form.lock_disclaimer": "Tua ratio non est {clausa}. Quisquis te sequi potest ut visum accipiat nuntios tuos tantum pro sectatoribus.",
|
||||||
"compose_form.lock_disclaimer.lock": "clausum",
|
"compose_form.lock_disclaimer.lock": "clausum",
|
||||||
"compose_form.placeholder": "What is on your mind?",
|
"compose_form.placeholder": "What is on your mind?",
|
||||||
"compose_form.publish_form": "Barrire",
|
"compose_form.publish_form": "Barrire",
|
||||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
"compose_form.spoiler.unmarked": "Adde praeconium contentūs",
|
||||||
"confirmations.block.confirm": "Impedire",
|
"confirmations.block.confirm": "Impedire",
|
||||||
"confirmations.delete.confirm": "Oblitterare",
|
"confirmations.delete.confirm": "Oblitterare",
|
||||||
"confirmations.delete.message": "Are you sure you want to delete this status?",
|
"confirmations.delete.message": "Are you sure you want to delete this status?",
|
||||||
"confirmations.delete_list.confirm": "Oblitterare",
|
"confirmations.delete_list.confirm": "Oblitterare",
|
||||||
|
"confirmations.discard_edit_media.message": "Habēs mutationēs in descriptionem vel prōspectum medii quae nōn sunt servātae; eas dēmittam?",
|
||||||
"confirmations.mute.confirm": "Confutare",
|
"confirmations.mute.confirm": "Confutare",
|
||||||
"confirmations.reply.confirm": "Respondere",
|
"confirmations.reply.confirm": "Respondere",
|
||||||
|
"disabled_account_banner.account_settings": "Praeferentiae ratiōnis",
|
||||||
|
"disabled_account_banner.text": "Ratio tua {disabledAccount} debilitata est.",
|
||||||
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
|
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
|
||||||
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
|
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
|
||||||
|
"domain_block_modal.you_will_lose_followers": "Omnes sectatores tuī ex hoc servō removēbuntur.",
|
||||||
|
"domain_block_modal.you_wont_see_posts": "Nuntios aut notificātiōnēs ab usoribus in hōc servō nōn vidēbis.",
|
||||||
|
"domain_pill.activitypub_like_language": "ActivityPub est velut lingua quam Mastodon cum aliīs sociālibus rētibus loquitur.",
|
||||||
|
"domain_pill.your_handle": "Tuus nominulus:",
|
||||||
|
"domain_pill.your_server": "Tua domus digitalis, ubi omnia tua nuntia habitant. Hanc non amas? Servēs trānsferāre potes quōcumque tempore et sectātōrēs tuōs simul addūcere.",
|
||||||
|
"domain_pill.your_username": "Tuō singulāre id indicium in hōc servō est. Est possibile invenīre usōrēs cum eōdem nōmine in servīs aliīs.",
|
||||||
"embed.instructions": "Embed this status on your website by copying the code below.",
|
"embed.instructions": "Embed this status on your website by copying the code below.",
|
||||||
|
"emoji_button.activity": "Actiō",
|
||||||
"emoji_button.food": "Cibus et potus",
|
"emoji_button.food": "Cibus et potus",
|
||||||
"emoji_button.people": "Homines",
|
"emoji_button.people": "Homines",
|
||||||
"emoji_button.search": "Quaerere...",
|
"emoji_button.search": "Quaerere...",
|
||||||
|
"empty_column.account_suspended": "Rātiō suspēnsa",
|
||||||
"empty_column.account_timeline": "Hic nulla contributa!",
|
"empty_column.account_timeline": "Hic nulla contributa!",
|
||||||
"empty_column.account_unavailable": "Notio non impetrabilis",
|
"empty_column.account_unavailable": "Notio non impetrabilis",
|
||||||
"empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
|
"empty_column.blocks": "Nondum quemquam usorem obsēcāvisti.",
|
||||||
|
"empty_column.direct": "Nōn habēs adhūc ullo mentionēs prīvātās. Cum ūnam mīseris aut accipis, hīc apparēbit.",
|
||||||
|
"empty_column.followed_tags": "Nōn adhūc aliquem hastāginem secūtus es. Cum id fēceris, hic ostendētur.",
|
||||||
|
"empty_column.home": "Tua linea temporum domesticus vacua est! Sequere plures personas ut eam compleas.",
|
||||||
"empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
|
"empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
|
||||||
|
"empty_column.lists": "Nōn adhūc habēs ullo tabellās. Cum creās, hīc apparēbunt.",
|
||||||
|
"empty_column.mutes": "Nondum quemquam usorem tacuisti.",
|
||||||
|
"empty_column.notification_requests": "Omnia clara sunt! Nihil hic est. Cum novās notificātiōnēs accipīs, hic secundum tua praecepta apparebunt.",
|
||||||
|
"empty_column.notifications": "Nōn adhūc habēs ullo notificātiōnēs. Cum aliī tē interagunt, hīc videbis.",
|
||||||
"explore.trending_statuses": "Contributa",
|
"explore.trending_statuses": "Contributa",
|
||||||
|
"filtered_notifications_banner.mentions": "{count, plural, one {mentiō} other {mentiōnēs}}",
|
||||||
|
"firehose.all": "Omnis",
|
||||||
|
"footer.about": "De",
|
||||||
"generic.saved": "Servavit",
|
"generic.saved": "Servavit",
|
||||||
|
"hashtag.column_settings.tag_mode.all": "Haec omnia",
|
||||||
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
|
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
|
||||||
|
"hashtag.counter_by_accounts": "{count, plural, one {{counter} particeps} other {{counter} participēs}}",
|
||||||
|
"hashtag.counter_by_uses": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}}",
|
||||||
|
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}} hodie",
|
||||||
|
"hashtags.and_other": "…et {count, plural, other {# plus}}",
|
||||||
|
"intervals.full.days": "{number, plural, one {# die} other {# dies}}",
|
||||||
|
"intervals.full.hours": "{number, plural, one {# hora} other {# horae}}",
|
||||||
|
"intervals.full.minutes": "{number, plural, one {# minutum} other {# minuta}}",
|
||||||
"keyboard_shortcuts.back": "Re navigare",
|
"keyboard_shortcuts.back": "Re navigare",
|
||||||
"keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum",
|
"keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum",
|
||||||
"keyboard_shortcuts.boost": "Inlustrare publicatio",
|
"keyboard_shortcuts.boost": "Inlustrare publicatio",
|
||||||
|
@ -89,17 +131,47 @@
|
||||||
"keyboard_shortcuts.up": "to move up in the list",
|
"keyboard_shortcuts.up": "to move up in the list",
|
||||||
"lightbox.close": "Claudere",
|
"lightbox.close": "Claudere",
|
||||||
"lightbox.next": "Secundum",
|
"lightbox.next": "Secundum",
|
||||||
|
"lists.account.add": "Adde ad tabellās",
|
||||||
|
"lists.new.create": "Addere tabella",
|
||||||
|
"load_pending": "{count, plural, one {# novum item} other {# nova itema}}",
|
||||||
|
"media_gallery.toggle_visible": "{number, plural, one {Cēla imaginem} other {Cēla imagines}}",
|
||||||
|
"moved_to_account_banner.text": "Tua ratione {disabledAccount} interdum reposita est, quod ad {movedToAccount} migrāvisti.",
|
||||||
|
"mute_modal.you_wont_see_mentions": "Non videbis nuntios quī eōs commemorant.",
|
||||||
|
"navigation_bar.about": "De",
|
||||||
"navigation_bar.domain_blocks": "Hidden domains",
|
"navigation_bar.domain_blocks": "Hidden domains",
|
||||||
"not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
|
"not_signed_in_indicator.not_signed_in": "Ad hunc locum pervenire oportet ut inīre facias.",
|
||||||
"notification.reblog": "{name} boosted your status",
|
"notification.admin.report": "{name} nuntiavit {target}",
|
||||||
|
"notification.admin.sign_up": "{name} subscripsit",
|
||||||
|
"notification.favourite": "{name} nuntium tuum favit",
|
||||||
|
"notification.follow": "{name} te secutus est",
|
||||||
|
"notification.follow_request": "{name} postulavit ut te sequeretur",
|
||||||
|
"notification.mention": "{name} memoravi",
|
||||||
|
"notification.moderation_warning": "Accepistī monitionem moderationis.",
|
||||||
|
"notification.moderation_warning.action_disable": "Ratio tua debilitata est.",
|
||||||
|
"notification.moderation_warning.action_none": "Tua ratiō monitum moderātiōnis accēpit.",
|
||||||
|
"notification.moderation_warning.action_sensitive": "Tua nuntia hinc sensibiliter notabuntur.",
|
||||||
|
"notification.moderation_warning.action_silence": "Ratio tua est limitata.",
|
||||||
|
"notification.moderation_warning.action_suspend": "Ratio tua suspensus est.",
|
||||||
|
"notification.own_poll": "Suffragium tuum terminatum est.",
|
||||||
|
"notification.poll": "Electione in quam suffragium dedisti finita est.",
|
||||||
|
"notification.reblog": "{name} tuum nuntium amplificavit.",
|
||||||
|
"notification.relationships_severance_event.account_suspension": "Admin ab {from} {target} suspendit, quod significat nōn iam posse tē novitātēs ab eīs accipere aut cum eīs interagere.",
|
||||||
|
"notification.relationships_severance_event.domain_block": "Admin ab {from} {target} obsēcāvit, includēns {followersCount} ex tuīs sectātōribus et {followingCount, plural, one {# ratione} other {# rationibus}} quās sequeris.",
|
||||||
|
"notification.relationships_severance_event.user_domain_block": "Bloqueāstī {target}, removēns {followersCount} ex sectātōribus tuīs et {followingCount, plural, one {# rationem} other {# rationēs}} quōs sequeris.",
|
||||||
|
"notification.status": "{name} nuper publicavit",
|
||||||
|
"notification.update": "{name} nuntium correxit",
|
||||||
|
"notification_requests.accept": "Accipe",
|
||||||
"notifications.filter.all": "Omnia",
|
"notifications.filter.all": "Omnia",
|
||||||
"notifications.filter.polls": "Eventus electionis",
|
"notifications.filter.polls": "Eventus electionis",
|
||||||
|
"notifications.group": "Notificātiōnēs",
|
||||||
"onboarding.actions.go_to_explore": "See what's trending",
|
"onboarding.actions.go_to_explore": "See what's trending",
|
||||||
"onboarding.actions.go_to_home": "Go to your home feed",
|
"onboarding.actions.go_to_home": "Go to your home feed",
|
||||||
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
|
"onboarding.follows.lead": "Tua domus feed est principalis via Mastodon experīrī. Quō plūrēs persōnas sequeris, eō actīvior et interessantior erit. Ad tē incipiendum, ecce quaedam suāsiones:",
|
||||||
"onboarding.follows.title": "Popular on Mastodon",
|
"onboarding.follows.title": "Popular on Mastodon",
|
||||||
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
|
"onboarding.profile.display_name_hint": "Tuum nomen completum aut tuum nomen ludens...",
|
||||||
|
"onboarding.start.lead": "Nunc pars es Mastodonis, singularis, socialis medii platformae decentralis ubi—non algorismus—tuam ipsius experientiam curas. Incipiāmus in nova hac socialis regione:",
|
||||||
"onboarding.start.skip": "Want to skip right ahead?",
|
"onboarding.start.skip": "Want to skip right ahead?",
|
||||||
|
"onboarding.start.title": "Perfecisti eam!",
|
||||||
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
|
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
|
||||||
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
|
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
|
||||||
"onboarding.steps.publish_status.body": "Say hello to the world.",
|
"onboarding.steps.publish_status.body": "Say hello to the world.",
|
||||||
|
@ -107,29 +179,48 @@
|
||||||
"onboarding.steps.setup_profile.title": "Customize your profile",
|
"onboarding.steps.setup_profile.title": "Customize your profile",
|
||||||
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
||||||
"onboarding.steps.share_profile.title": "Share your profile",
|
"onboarding.steps.share_profile.title": "Share your profile",
|
||||||
|
"onboarding.tips.2fa": "<strong>Scisne?</strong> Tūam ratiōnem sēcūrāre potes duōrum elementōrum authentīcātiōnem in ratiōnis tuī praeferentiīs statuendō. Cum ūllā app TOTP ex tuā ēlēctiōne operātur, numerus tēlephōnicus necessārius nōn est!",
|
||||||
|
"onboarding.tips.accounts_from_other_servers": "<strong>Scisne?</strong> Quoniam Mastodon dēcentrālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!",
|
||||||
|
"onboarding.tips.migration": "<strong>Scisne?</strong> Sī sentīs {domain} tibi in futūrō nōn esse optimam servī ēlēctiōnem, ad alium servum Mastodon sine amittendō sectātōribus tuīs migrāre potes. Etiam tuum servum hospitārī potes!",
|
||||||
|
"onboarding.tips.verification": "<strong>Scisne?</strong> Tūam ratiōnem verificāre potes iungendō nexum ad prōfīlium Mastodon tuum in propriā pāginā interrētiā et addendō pāginam ad prōfīlium tuum. Nullae pecūniae aut documenta necessāria sunt!",
|
||||||
"poll.closed": "Clausum",
|
"poll.closed": "Clausum",
|
||||||
|
"poll.total_people": "{count, plural, one {# persona} other {# personae}}",
|
||||||
|
"poll.total_votes": "{count, plural, one {# suffragium} other {# suffragia}}",
|
||||||
"poll.vote": "Eligere",
|
"poll.vote": "Eligere",
|
||||||
"poll.voted": "Elegisti hoc responsum",
|
"poll.voted": "Elegisti hoc responsum",
|
||||||
|
"poll.votes": "{votes, plural, one {# sufragium} other {# sufragia}}",
|
||||||
"poll_button.add_poll": "Addere electionem",
|
"poll_button.add_poll": "Addere electionem",
|
||||||
"poll_button.remove_poll": "Auferre electionem",
|
"poll_button.remove_poll": "Auferre electionem",
|
||||||
"privacy.change": "Adjust status privacy",
|
"privacy.change": "Adjust status privacy",
|
||||||
"privacy.public.short": "Coram publico",
|
"privacy.public.short": "Coram publico",
|
||||||
|
"regeneration_indicator.sublabel": "Tua domus feed praeparātur!",
|
||||||
|
"relative_time.full.days": "{number, plural, one {# ante die} other {# ante dies}}",
|
||||||
|
"relative_time.full.hours": "{number, plural, one {# ante horam} other {# ante horas}}",
|
||||||
"relative_time.full.just_now": "nunc",
|
"relative_time.full.just_now": "nunc",
|
||||||
|
"relative_time.full.minutes": "{number, plural, one {# ante minutum} other {# ante minuta}}",
|
||||||
|
"relative_time.full.seconds": "{number, plural, one {# ante secundum} other {# ante secunda}}",
|
||||||
"relative_time.just_now": "nunc",
|
"relative_time.just_now": "nunc",
|
||||||
"relative_time.today": "hodie",
|
"relative_time.today": "hodie",
|
||||||
|
"reply_indicator.attachments": "{count, plural, one {# annexus} other {# annexūs}}",
|
||||||
"report.block": "Impedimentum",
|
"report.block": "Impedimentum",
|
||||||
|
"report.block_explanation": "Non videbis eorum nuntios. Non poterunt vidēre tuōs nuntios aut tē sequī. Intelligere poterunt sē obstrūctōs esse.",
|
||||||
"report.categories.other": "Altera",
|
"report.categories.other": "Altera",
|
||||||
"report.category.title_account": "notio",
|
"report.category.title_account": "notio",
|
||||||
"report.category.title_status": "contributum",
|
"report.category.title_status": "contributum",
|
||||||
"report.close": "Confectum",
|
"report.close": "Confectum",
|
||||||
"report.mute": "Confutare",
|
"report.mute": "Confutare",
|
||||||
|
"report.mute_explanation": "Non videbis eōrum nuntiōs. Possunt adhuc tē sequī et tuōs nuntiōs vidēre, nec sciēbunt sē tacitōs esse.",
|
||||||
"report.next": "Secundum",
|
"report.next": "Secundum",
|
||||||
"report.placeholder": "Type or paste additional comments",
|
"report.placeholder": "Commentāriī adiūnctī",
|
||||||
"report.submit": "Mittere",
|
"report.submit": "Mittere",
|
||||||
"report.target": "Report {target}",
|
"report.target": "Report {target}",
|
||||||
"report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached",
|
"report_notification.attached_statuses": "{count, plural, one {{count} nuntius} other {{count} nuntii}} attachiatus",
|
||||||
"report_notification.categories.other": "Altera",
|
"report_notification.categories.other": "Altera",
|
||||||
"search.placeholder": "Quaerere",
|
"search.placeholder": "Quaerere",
|
||||||
|
"search_results.all": "Omnis",
|
||||||
|
"server_banner.active_users": "Usūrāriī āctīvī",
|
||||||
|
"server_banner.administered_by": "Administratur:",
|
||||||
|
"server_banner.introduction": "{domain} pars est de rete sociali decentralizato a {mastodon} propulsato.",
|
||||||
"server_banner.learn_more": "Discere plura",
|
"server_banner.learn_more": "Discere plura",
|
||||||
"sign_in_banner.sign_in": "Sign in",
|
"sign_in_banner.sign_in": "Sign in",
|
||||||
"status.admin_status": "Open this status in the moderation interface",
|
"status.admin_status": "Open this status in the moderation interface",
|
||||||
|
@ -139,13 +230,29 @@
|
||||||
"status.delete": "Oblitterare",
|
"status.delete": "Oblitterare",
|
||||||
"status.edit": "Recolere",
|
"status.edit": "Recolere",
|
||||||
"status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}",
|
"status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}",
|
||||||
|
"status.favourites": "{count, plural, one {favoritum} other {favorita}}",
|
||||||
|
"status.history.created": "{name} creatum {date}",
|
||||||
|
"status.history.edited": "{name} correxit {date}",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
"status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}",
|
"status.reblogged_by": "{name} adiuvavit",
|
||||||
|
"status.reblogs": "{count, plural, one {auctus} other {auctūs}}",
|
||||||
|
"status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}",
|
||||||
"tabs_bar.home": "Domi",
|
"tabs_bar.home": "Domi",
|
||||||
|
"time_remaining.days": "{number, plural, one {# die} other {# dies}} restant",
|
||||||
|
"time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant",
|
||||||
|
"time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant",
|
||||||
|
"time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant",
|
||||||
|
"timeline_hint.remote_resource_not_displayed": "{resource} ab aliīs servīs nōn ostenduntur.",
|
||||||
"timeline_hint.resources.statuses": "Contributa pristina",
|
"timeline_hint.resources.statuses": "Contributa pristina",
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {diē prīdiē} other {diēbus praeteritīs {days}}}",
|
||||||
|
"ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.",
|
||||||
|
"units.short.billion": "{count} millia milionum",
|
||||||
|
"units.short.million": "{count} milionum",
|
||||||
|
"units.short.thousand": "{count} millia",
|
||||||
|
"upload_button.label": "Imaginēs, vīdeō aut fīle audītūs adde",
|
||||||
"upload_form.audio_description": "Describe for people who are hard of hearing",
|
"upload_form.audio_description": "Describe for people who are hard of hearing",
|
||||||
"upload_form.edit": "Recolere",
|
"upload_form.edit": "Recolere",
|
||||||
|
"upload_modal.description_placeholder": "A velox brunneis vulpes salit super piger canis",
|
||||||
"upload_progress.label": "Uploading…",
|
"upload_progress.label": "Uploading…",
|
||||||
"video.mute": "Confutare soni"
|
"video.mute": "Confutare soni"
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,7 +217,7 @@
|
||||||
"domain_block_modal.title": "Blokuoti domeną?",
|
"domain_block_modal.title": "Blokuoti domeną?",
|
||||||
"domain_block_modal.you_will_lose_followers": "Visi tavo sekėjai iš šio serverio bus pašalinti.",
|
"domain_block_modal.you_will_lose_followers": "Visi tavo sekėjai iš šio serverio bus pašalinti.",
|
||||||
"domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.",
|
"domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.",
|
||||||
"domain_pill.activitypub_lets_connect": "Tai leidžia tau sąveikauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
|
"domain_pill.activitypub_lets_connect": "Tai leidžia tau prisijungti ir bendrauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
|
||||||
"domain_pill.activitypub_like_language": "ActivityPub – tai tarsi kalba, kuria Mastodon kalba su kitais socialiniais tinklais.",
|
"domain_pill.activitypub_like_language": "ActivityPub – tai tarsi kalba, kuria Mastodon kalba su kitais socialiniais tinklais.",
|
||||||
"domain_pill.server": "Serveris",
|
"domain_pill.server": "Serveris",
|
||||||
"domain_pill.their_handle": "Jų socialinis medijos vardas:",
|
"domain_pill.their_handle": "Jų socialinis medijos vardas:",
|
||||||
|
@ -433,7 +433,15 @@
|
||||||
"loading_indicator.label": "Kraunama…",
|
"loading_indicator.label": "Kraunama…",
|
||||||
"media_gallery.toggle_visible": "{number, plural, one {Slėpti vaizdą} few {Slėpti vaizdus} many {Slėpti vaizdo} other {Slėpti vaizdų}}",
|
"media_gallery.toggle_visible": "{number, plural, one {Slėpti vaizdą} few {Slėpti vaizdus} many {Slėpti vaizdo} other {Slėpti vaizdų}}",
|
||||||
"moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta, nes persikėlei į {movedToAccount}.",
|
"moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta, nes persikėlei į {movedToAccount}.",
|
||||||
|
"mute_modal.hide_from_notifications": "Slėpti nuo pranešimų",
|
||||||
|
"mute_modal.hide_options": "Slėpti parinktis",
|
||||||
|
"mute_modal.indefinite": "Kol atšauksiu jų nutildymą",
|
||||||
"mute_modal.show_options": "Rodyti parinktis",
|
"mute_modal.show_options": "Rodyti parinktis",
|
||||||
|
"mute_modal.they_can_mention_and_follow": "Jie gali tave paminėti ir sekti, bet tu jų nematysi.",
|
||||||
|
"mute_modal.they_wont_know": "Jie nežinos, kad buvo nutildyti.",
|
||||||
|
"mute_modal.title": "Nutildyti naudotoją?",
|
||||||
|
"mute_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.",
|
||||||
|
"mute_modal.you_wont_see_posts": "Jie vis tiek gali matyti tavo įrašus, bet tu nematysi jų.",
|
||||||
"navigation_bar.about": "Apie",
|
"navigation_bar.about": "Apie",
|
||||||
"navigation_bar.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają",
|
"navigation_bar.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają",
|
||||||
"navigation_bar.blocks": "Užblokuoti naudotojai",
|
"navigation_bar.blocks": "Užblokuoti naudotojai",
|
||||||
|
@ -478,6 +486,7 @@
|
||||||
"notification.own_poll": "Tavo apklausa baigėsi",
|
"notification.own_poll": "Tavo apklausa baigėsi",
|
||||||
"notification.poll": "Apklausa, kurioje balsavai, pasibaigė",
|
"notification.poll": "Apklausa, kurioje balsavai, pasibaigė",
|
||||||
"notification.reblog": "{name} pakėlė tavo įrašą",
|
"notification.reblog": "{name} pakėlė tavo įrašą",
|
||||||
|
"notification.relationships_severance_event": "Prarasti sąryšiai su {name}",
|
||||||
"notification.relationships_severance_event.learn_more": "Sužinoti daugiau",
|
"notification.relationships_severance_event.learn_more": "Sužinoti daugiau",
|
||||||
"notification.relationships_severance_event.user_domain_block": "Tu užblokavai {target}. Pašalinama {followersCount} savo sekėjų ir {followingCount, plural, one {# paskyrą} few {# paskyrai} many {# paskyros} other {# paskyrų}}, kurios seki.",
|
"notification.relationships_severance_event.user_domain_block": "Tu užblokavai {target}. Pašalinama {followersCount} savo sekėjų ir {followingCount, plural, one {# paskyrą} few {# paskyrai} many {# paskyros} other {# paskyrų}}, kurios seki.",
|
||||||
"notification.status": "{name} ką tik paskelbė",
|
"notification.status": "{name} ką tik paskelbė",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Dit profiel is door de moderatoren van {domain} verborgen.",
|
"limited_account_hint.title": "Dit profiel is door de moderatoren van {domain} verborgen.",
|
||||||
"link_preview.author": "Door {name}",
|
"link_preview.author": "Door {name}",
|
||||||
"link_preview.more_from_author": "Meer van {name}",
|
"link_preview.more_from_author": "Meer van {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} bericht} other {{counter} berichten}}",
|
||||||
"lists.account.add": "Aan lijst toevoegen",
|
"lists.account.add": "Aan lijst toevoegen",
|
||||||
"lists.account.remove": "Uit lijst verwijderen",
|
"lists.account.remove": "Uit lijst verwijderen",
|
||||||
"lists.delete": "Lijst verwijderen",
|
"lists.delete": "Lijst verwijderen",
|
||||||
|
|
|
@ -414,6 +414,8 @@
|
||||||
"limited_account_hint.action": "Vis profilen likevel",
|
"limited_account_hint.action": "Vis profilen likevel",
|
||||||
"limited_account_hint.title": "Denne profilen er skjult av moderatorane på {domain}.",
|
"limited_account_hint.title": "Denne profilen er skjult av moderatorane på {domain}.",
|
||||||
"link_preview.author": "Av {name}",
|
"link_preview.author": "Av {name}",
|
||||||
|
"link_preview.more_from_author": "Meir frå {name}",
|
||||||
|
"link_preview.shares": "{count, plural,one {{counter} innlegg} other {{counter} innlegg}}",
|
||||||
"lists.account.add": "Legg til i liste",
|
"lists.account.add": "Legg til i liste",
|
||||||
"lists.account.remove": "Fjern frå liste",
|
"lists.account.remove": "Fjern frå liste",
|
||||||
"lists.delete": "Slett liste",
|
"lists.delete": "Slett liste",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Ten profil został ukryty przez moderatorów {domain}.",
|
"limited_account_hint.title": "Ten profil został ukryty przez moderatorów {domain}.",
|
||||||
"link_preview.author": "{name}",
|
"link_preview.author": "{name}",
|
||||||
"link_preview.more_from_author": "Więcej od {name}",
|
"link_preview.more_from_author": "Więcej od {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}}",
|
||||||
"lists.account.add": "Dodaj do listy",
|
"lists.account.add": "Dodaj do listy",
|
||||||
"lists.account.remove": "Usunąć z listy",
|
"lists.account.remove": "Usunąć z listy",
|
||||||
"lists.delete": "Usuń listę",
|
"lists.delete": "Usuń listę",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Este perfil foi ocultado pelos moderadores de {domain}.",
|
"limited_account_hint.title": "Este perfil foi ocultado pelos moderadores de {domain}.",
|
||||||
"link_preview.author": "Por {name}",
|
"link_preview.author": "Por {name}",
|
||||||
"link_preview.more_from_author": "Mais de {name}",
|
"link_preview.more_from_author": "Mais de {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} publicação} other {{counter} publicações}}",
|
||||||
"lists.account.add": "Adicionar à lista",
|
"lists.account.add": "Adicionar à lista",
|
||||||
"lists.account.remove": "Remover da lista",
|
"lists.account.remove": "Remover da lista",
|
||||||
"lists.delete": "Eliminar lista",
|
"lists.delete": "Eliminar lista",
|
||||||
|
|
|
@ -391,6 +391,7 @@
|
||||||
"limited_account_hint.action": "Aj tak zobraziť profil",
|
"limited_account_hint.action": "Aj tak zobraziť profil",
|
||||||
"limited_account_hint.title": "Tento profil bol skrytý správcami servera {domain}.",
|
"limited_account_hint.title": "Tento profil bol skrytý správcami servera {domain}.",
|
||||||
"link_preview.author": "Autor: {name}",
|
"link_preview.author": "Autor: {name}",
|
||||||
|
"link_preview.more_from_author": "Viac od {name}",
|
||||||
"lists.account.add": "Pridať do zoznamu",
|
"lists.account.add": "Pridať do zoznamu",
|
||||||
"lists.account.remove": "Odstrániť zo zoznamu",
|
"lists.account.remove": "Odstrániť zo zoznamu",
|
||||||
"lists.delete": "Vymazať zoznam",
|
"lists.delete": "Vymazať zoznam",
|
||||||
|
@ -411,6 +412,7 @@
|
||||||
"moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálne deaktivovaný, pretože ste sa presunuli na {movedToAccount}.",
|
"moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálne deaktivovaný, pretože ste sa presunuli na {movedToAccount}.",
|
||||||
"mute_modal.hide_from_notifications": "Ukryť z upozornení",
|
"mute_modal.hide_from_notifications": "Ukryť z upozornení",
|
||||||
"mute_modal.hide_options": "Skryť možnosti",
|
"mute_modal.hide_options": "Skryť možnosti",
|
||||||
|
"mute_modal.indefinite": "Pokiaľ ich neodtíšim",
|
||||||
"mute_modal.show_options": "Zobraziť možnosti",
|
"mute_modal.show_options": "Zobraziť možnosti",
|
||||||
"mute_modal.title": "Stíšiť užívateľa?",
|
"mute_modal.title": "Stíšiť užívateľa?",
|
||||||
"navigation_bar.about": "O tomto serveri",
|
"navigation_bar.about": "O tomto serveri",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Profil so moderatorji strežnika {domain} skrili.",
|
"limited_account_hint.title": "Profil so moderatorji strežnika {domain} skrili.",
|
||||||
"link_preview.author": "Avtor_ica {name}",
|
"link_preview.author": "Avtor_ica {name}",
|
||||||
"link_preview.more_from_author": "Več od {name}",
|
"link_preview.more_from_author": "Več od {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objave} other {{counter} objav}}",
|
||||||
"lists.account.add": "Dodaj na seznam",
|
"lists.account.add": "Dodaj na seznam",
|
||||||
"lists.account.remove": "Odstrani s seznama",
|
"lists.account.remove": "Odstrani s seznama",
|
||||||
"lists.delete": "Izbriši seznam",
|
"lists.delete": "Izbriši seznam",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "Denna profil har dolts av {domain}s moderatorer.",
|
"limited_account_hint.title": "Denna profil har dolts av {domain}s moderatorer.",
|
||||||
"link_preview.author": "Av {name}",
|
"link_preview.author": "Av {name}",
|
||||||
"link_preview.more_from_author": "Mer från {name}",
|
"link_preview.more_from_author": "Mer från {name}",
|
||||||
|
"link_preview.shares": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}}",
|
||||||
"lists.account.add": "Lägg till i lista",
|
"lists.account.add": "Lägg till i lista",
|
||||||
"lists.account.remove": "Ta bort från lista",
|
"lists.account.remove": "Ta bort från lista",
|
||||||
"lists.delete": "Radera lista",
|
"lists.delete": "Radera lista",
|
||||||
|
|
|
@ -26,8 +26,9 @@
|
||||||
"account.featured_tags.last_status_never": "کوئی مراسلہ نہیں",
|
"account.featured_tags.last_status_never": "کوئی مراسلہ نہیں",
|
||||||
"account.featured_tags.title": "{name} کے نمایاں ہیش ٹیگز",
|
"account.featured_tags.title": "{name} کے نمایاں ہیش ٹیگز",
|
||||||
"account.follow": "پیروی کریں",
|
"account.follow": "پیروی کریں",
|
||||||
|
"account.follow_back": "اکاؤنٹ کو فالو بیک ",
|
||||||
"account.followers": "پیروکار",
|
"account.followers": "پیروکار",
|
||||||
"account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
|
"account.followers.empty": "ہنوز اس صارف کی کوئی پیروی نہیں کرتا.",
|
||||||
"account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}",
|
"account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}",
|
||||||
"account.following": "فالو کر رہے ہیں",
|
"account.following": "فالو کر رہے ہیں",
|
||||||
"account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
|
"account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
"account.mute_notifications_short": "نوٹیفیکیشنز کو خاموش کریں",
|
"account.mute_notifications_short": "نوٹیفیکیشنز کو خاموش کریں",
|
||||||
"account.mute_short": "خاموش",
|
"account.mute_short": "خاموش",
|
||||||
"account.muted": "خاموش کردہ",
|
"account.muted": "خاموش کردہ",
|
||||||
|
"account.mutual": "میوچول اکاؤنٹ",
|
||||||
"account.no_bio": "کوئی تفصیل نہیں دی گئی۔",
|
"account.no_bio": "کوئی تفصیل نہیں دی گئی۔",
|
||||||
"account.open_original_page": "اصل صفحہ کھولیں",
|
"account.open_original_page": "اصل صفحہ کھولیں",
|
||||||
"account.posts": "ٹوٹ",
|
"account.posts": "ٹوٹ",
|
||||||
|
@ -64,7 +66,8 @@
|
||||||
"account.unmute": "@{name} کو با آواز کریں",
|
"account.unmute": "@{name} کو با آواز کریں",
|
||||||
"account.unmute_notifications_short": "نوٹیفیکیشنز کو خاموش نہ کریں",
|
"account.unmute_notifications_short": "نوٹیفیکیشنز کو خاموش نہ کریں",
|
||||||
"account.unmute_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.average": "اوسط",
|
||||||
"admin.dashboard.retention.cohort_size": "نئے یسرز",
|
"admin.dashboard.retention.cohort_size": "نئے یسرز",
|
||||||
"alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
|
"alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "此账号资料已被 {domain} 管理员隐藏。",
|
"limited_account_hint.title": "此账号资料已被 {domain} 管理员隐藏。",
|
||||||
"link_preview.author": "由 {name}",
|
"link_preview.author": "由 {name}",
|
||||||
"link_preview.more_from_author": "查看 {name} 的更多内容",
|
"link_preview.more_from_author": "查看 {name} 的更多内容",
|
||||||
|
"link_preview.shares": "{count, plural, other {{counter} 条嘟文}}",
|
||||||
"lists.account.add": "添加到列表",
|
"lists.account.add": "添加到列表",
|
||||||
"lists.account.remove": "从列表中移除",
|
"lists.account.remove": "从列表中移除",
|
||||||
"lists.delete": "删除列表",
|
"lists.delete": "删除列表",
|
||||||
|
|
|
@ -415,6 +415,7 @@
|
||||||
"limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。",
|
"limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。",
|
||||||
"link_preview.author": "來自 {name}",
|
"link_preview.author": "來自 {name}",
|
||||||
"link_preview.more_from_author": "來自 {name} 之更多內容",
|
"link_preview.more_from_author": "來自 {name} 之更多內容",
|
||||||
|
"link_preview.shares": "{count, plural, other {{count} 則嘟文}}",
|
||||||
"lists.account.add": "新增至列表",
|
"lists.account.add": "新增至列表",
|
||||||
"lists.account.remove": "自列表中移除",
|
"lists.account.remove": "自列表中移除",
|
||||||
"lists.delete": "刪除列表",
|
"lists.delete": "刪除列表",
|
||||||
|
|
|
@ -903,9 +903,15 @@ body > [data-popper-placement] {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 22px;
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -1411,10 +1417,15 @@ body > [data-popper-placement] {
|
||||||
.audio-player,
|
.audio-player,
|
||||||
.attachment-list,
|
.attachment-list,
|
||||||
.picture-in-picture-placeholder,
|
.picture-in-picture-placeholder,
|
||||||
|
.more-from-author,
|
||||||
.status-card,
|
.status-card,
|
||||||
.hashtag-bar {
|
.hashtag-bar {
|
||||||
margin-inline-start: $thread-margin;
|
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 {
|
.status__content__read-more-button {
|
||||||
|
@ -4129,6 +4140,13 @@ a.status-card {
|
||||||
border-end-start-radius: 0;
|
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 {
|
.status-card.expanded > a {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -8784,43 +8802,80 @@ noscript {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
text-decoration: none;
|
padding: 16px;
|
||||||
padding: 15px;
|
|
||||||
border-bottom: 1px solid var(--background-border-color);
|
border-bottom: 1px solid var(--background-border-color);
|
||||||
gap: 15px;
|
gap: 16px;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
color: $highlight-text-color;
|
|
||||||
|
|
||||||
.story__details__publisher,
|
|
||||||
.story__details__shared {
|
|
||||||
color: $highlight-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__details {
|
&__details {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
&__publisher {
|
&__publisher {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
|
display: block;
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: $primary-text-color;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__shared {
|
&__shared {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
color: $darker-text-color;
|
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 {
|
strong {
|
||||||
|
@ -8885,14 +8940,14 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
.server-banner {
|
.server-banner {
|
||||||
padding: 20px 0;
|
|
||||||
|
|
||||||
&__introduction {
|
&__introduction {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 22px;
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -8920,6 +8975,9 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: $darker-text-color;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9891,14 +9949,14 @@ noscript {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: $ui-base-color;
|
background: var(--surface-variant-background-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active {
|
||||||
background: lighten($ui-base-color, 4%);
|
background: var(--surface-variant-active-background-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10229,6 +10287,7 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-from-author {
|
.more-from-author {
|
||||||
|
box-sizing: border-box;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
background: var(--surface-background-color);
|
background: var(--surface-background-color);
|
||||||
|
|
|
@ -613,9 +613,10 @@ code {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
max-width: 140px;
|
max-width: 50%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
|
|
|
@ -106,4 +106,6 @@ $font-monospace: 'mastodon-font-monospace' !default;
|
||||||
--background-color: #{darken($ui-base-color, 8%)};
|
--background-color: #{darken($ui-base-color, 8%)};
|
||||||
--background-color-tint: #{rgba(darken($ui-base-color, 8%), 0.9)};
|
--background-color-tint: #{rgba(darken($ui-base-color, 8%), 0.9)};
|
||||||
--surface-background-color: #{darken($ui-base-color, 4%)};
|
--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%)};
|
||||||
}
|
}
|
||||||
|
|
10
app/lib/access_grant_extension.rb
Normal file
10
app/lib/access_grant_extension.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module AccessGrantExtension
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
|
||||||
|
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,10 @@ module AccessTokenExtension
|
||||||
has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token
|
has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token
|
||||||
|
|
||||||
after_commit :push_to_streaming_api
|
after_commit :push_to_streaming_api
|
||||||
|
|
||||||
|
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
|
||||||
|
scope :not_revoked, -> { where(revoked_at: nil) }
|
||||||
|
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def revoke(clock = Time)
|
def revoke(clock = Time)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::Activity::Flag < ActivityPub::Activity
|
class ActivityPub::Activity::Flag < ActivityPub::Activity
|
||||||
|
COMMENT_SIZE_LIMIT = 5000
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
return if skip_reports?
|
return if skip_reports?
|
||||||
|
|
||||||
|
@ -38,6 +40,6 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_comment
|
def report_comment
|
||||||
(@json['content'] || '')[0...5000]
|
(@json['content'] || '')[0...COMMENT_SIZE_LIMIT]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def perform_query
|
def perform_query
|
||||||
[mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version].compact
|
[mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version, libvips_version, imagemagick_version, ffmpeg_version].compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def mastodon_version
|
def mastodon_version
|
||||||
|
@ -28,8 +28,8 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
|
||||||
{
|
{
|
||||||
key: 'ruby',
|
key: 'ruby',
|
||||||
human_key: 'Ruby',
|
human_key: 'Ruby',
|
||||||
value: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
|
value: RUBY_DESCRIPTION,
|
||||||
human_value: RUBY_DESCRIPTION,
|
human_value: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,6 +71,45 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def libvips_version
|
||||||
|
return unless Rails.configuration.x.use_vips
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'libvips',
|
||||||
|
human_key: 'libvips',
|
||||||
|
value: Vips.version_string,
|
||||||
|
human_value: Vips.version_string,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def imagemagick_version
|
||||||
|
return if Rails.configuration.x.use_vips
|
||||||
|
|
||||||
|
version = `convert -version`.match(/Version: ImageMagick ([\d\.]+)/)[1]
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'imagemagick',
|
||||||
|
human_key: 'ImageMagick',
|
||||||
|
value: version,
|
||||||
|
human_value: version,
|
||||||
|
}
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def ffmpeg_version
|
||||||
|
version = `ffmpeg -version`.match(/ffmpeg version ([\d\.]+)/)[1]
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'ffmpeg',
|
||||||
|
human_key: 'FFmpeg',
|
||||||
|
value: version,
|
||||||
|
human_value: version,
|
||||||
|
}
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
def redis_info
|
def redis_info
|
||||||
@redis_info ||= if redis.is_a?(Redis::Namespace)
|
@redis_info ||= if redis.is_a?(Redis::Namespace)
|
||||||
redis.redis.info
|
redis.redis.info
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure
|
||||||
SELECT count(*) FROM new_accounts
|
SELECT count(*) FROM new_accounts
|
||||||
) AS value
|
) AS value
|
||||||
FROM (
|
FROM (
|
||||||
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
|
#{generated_series_days}
|
||||||
) AS axis
|
) AS axis
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measur
|
||||||
SELECT count(*) FROM new_followers
|
SELECT count(*) FROM new_followers
|
||||||
) AS value
|
) AS value
|
||||||
FROM (
|
FROM (
|
||||||
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
|
#{generated_series_days}
|
||||||
) AS axis
|
) AS axis
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure:
|
||||||
SELECT count(*) FROM new_follows
|
SELECT count(*) FROM new_follows
|
||||||
) AS value
|
) AS value
|
||||||
FROM (
|
FROM (
|
||||||
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
|
#{generated_series_days}
|
||||||
) AS axis
|
) AS axis
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue