th: Merge remote-tracking branch 'glitch/main'
This commit is contained in:
commit
f8028d190e
1493 changed files with 23506 additions and 24764 deletions
|
@ -1,7 +1,7 @@
|
||||||
LOCAL_DOMAIN=localhost
|
LOCAL_DOMAIN=localhost
|
||||||
ALTERNATE_DOMAINS=mastodon.internal
|
ALTERNATE_DOMAINS=mastodon.internal
|
||||||
|
|
||||||
DB_HOST=$(pwd)/data/postgres
|
DB_HOST=$PWD/data/postgres
|
||||||
DB_USER=mastodon
|
DB_USER=mastodon
|
||||||
DB_NAME=mastodon_dev
|
DB_NAME=mastodon_dev
|
||||||
REDIS_URL=unix://./data/redis/redis-dev.sock
|
REDIS_URL=unix://./data/redis/redis-dev.sock
|
||||||
|
|
26
.eslintrc.js
26
.eslintrc.js
|
@ -247,7 +247,12 @@ module.exports = {
|
||||||
},
|
},
|
||||||
// Internal packages
|
// Internal packages
|
||||||
{
|
{
|
||||||
pattern: '{mastodon/**,flavours/glitch-soc/**}',
|
pattern: '{mastodon/**}',
|
||||||
|
group: 'internal',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: '{flavours/glitch-soc/**}',
|
||||||
group: 'internal',
|
group: 'internal',
|
||||||
position: 'after',
|
position: 'after',
|
||||||
},
|
},
|
||||||
|
@ -256,6 +261,18 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Forbid imports from vanilla in glitch flavour
|
||||||
|
'import/no-restricted-paths': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
zones: [{
|
||||||
|
target: 'app/javascript/flavours/glitch/',
|
||||||
|
from: 'app/javascript/mastodon/',
|
||||||
|
message: 'Import from /flavours/glitch/ instead'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
'promise/always-return': 'off',
|
'promise/always-return': 'off',
|
||||||
'promise/catch-or-return': [
|
'promise/catch-or-return': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -325,8 +342,8 @@ module.exports = {
|
||||||
|
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/strict-type-checked',
|
||||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
'plugin:@typescript-eslint/stylistic-type-checked',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'plugin:react-hooks/recommended',
|
'plugin:react-hooks/recommended',
|
||||||
'plugin:jsx-a11y/recommended',
|
'plugin:jsx-a11y/recommended',
|
||||||
|
@ -338,7 +355,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: './tsconfig.json',
|
project: true,
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -348,6 +365,7 @@ module.exports = {
|
||||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||||
'@typescript-eslint/consistent-type-exports': 'error',
|
'@typescript-eslint/consistent-type-exports': 'error',
|
||||||
'@typescript-eslint/consistent-type-imports': 'error',
|
'@typescript-eslint/consistent-type-imports': 'error',
|
||||||
|
"@typescript-eslint/prefer-nullish-coalescing": ['error', {ignorePrimitives: {boolean: true}}],
|
||||||
|
|
||||||
'jsdoc/require-jsdoc': 'off',
|
'jsdoc/require-jsdoc': 'off',
|
||||||
|
|
||||||
|
|
56
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
56
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
|
@ -1,56 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: If something isn't working as expected
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Make sure that you are submitting a new bug that was not previously reported or already fixed.
|
|
||||||
|
|
||||||
Please use a concise and distinct title for the issue.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce the problem
|
|
||||||
description: What were you trying to do?
|
|
||||||
value: |
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Expected behaviour
|
|
||||||
description: What should have happened?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Actual behaviour
|
|
||||||
description: What happened?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Detailed description
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Specifications
|
|
||||||
description: |
|
|
||||||
What version or commit hash of Mastodon did you find this bug in?
|
|
||||||
|
|
||||||
If a front-end issue, what browser and operating systems were you using?
|
|
||||||
placeholder: |
|
|
||||||
Mastodon 3.5.3 (or Edge)
|
|
||||||
Ruby 2.7.6 (or v3.1.2)
|
|
||||||
Node.js 16.18.0
|
|
||||||
|
|
||||||
Google Chrome 106.0.5249.119
|
|
||||||
Firefox 105.0.3
|
|
||||||
|
|
||||||
etc...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
76
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
Normal file
76
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
name: Bug Report (Web Interface)
|
||||||
|
description: If you are using Mastodon's web interface and something is not working as expected
|
||||||
|
labels: [bug, 'status/to triage', 'area/web interface']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Make sure that you are submitting a new bug that was not previously reported or already fixed.
|
||||||
|
|
||||||
|
Please use a concise and distinct title for the issue.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce the problem
|
||||||
|
description: What were you trying to do?
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: What should have happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Actual behaviour
|
||||||
|
description: What happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Detailed description
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon instance
|
||||||
|
description: The address of the Mastodon instance where you experienced the issue
|
||||||
|
placeholder: mastodon.social
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon version
|
||||||
|
description: |
|
||||||
|
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
||||||
|
placeholder: v4.1.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Browser name and version
|
||||||
|
description: |
|
||||||
|
What browser are you using when getting this bug? Please specify the version as well.
|
||||||
|
placeholder: Firefox 105.0.3
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Operating system
|
||||||
|
description: |
|
||||||
|
What OS are you running? Please specify the version as well.
|
||||||
|
placeholder: macOS 13.4.1
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Technical details
|
||||||
|
description: |
|
||||||
|
Any additional technical details you may have. This can include the full error log, inspector's output…
|
||||||
|
validations:
|
||||||
|
required: false
|
65
.github/ISSUE_TEMPLATE/2.server_bug_report.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/2.server_bug_report.yml
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
name: Bug Report (server / API)
|
||||||
|
description: |
|
||||||
|
If something is not working as expected, but is not from using the web interface.
|
||||||
|
labels: [bug, 'status/to triage']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Make sure that you are submitting a new bug that was not previously reported or already fixed.
|
||||||
|
|
||||||
|
Please use a concise and distinct title for the issue.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce the problem
|
||||||
|
description: What were you trying to do?
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: What should have happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Actual behaviour
|
||||||
|
description: What happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Detailed description
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon instance
|
||||||
|
description: The address of the Mastodon instance where you experienced the issue
|
||||||
|
placeholder: mastodon.social
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon version
|
||||||
|
description: |
|
||||||
|
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
||||||
|
placeholder: v4.1.2
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Technical details
|
||||||
|
description: |
|
||||||
|
Any additional technical details you may have, like logs or error traces
|
||||||
|
value: |
|
||||||
|
If this is happening on your own Mastodon server, please fill out those:
|
||||||
|
- Ruby version: (from `ruby --version`, eg. v3.1.2)
|
||||||
|
- Node.js version: (from `node --version`, eg. v18.16.0)
|
||||||
|
validations:
|
||||||
|
required: false
|
31
.github/renovate.json5
vendored
31
.github/renovate.json5
vendored
|
@ -9,13 +9,14 @@
|
||||||
],
|
],
|
||||||
stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
|
stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
|
||||||
// packageRules order is important, they are applied from top to bottom and are merged,
|
// packageRules order is important, they are applied from top to bottom and are merged,
|
||||||
// so for example grouping rules needs to be at the bottom
|
// meaning the most important ones must be at the bottom, for example grouping rules
|
||||||
|
// If we do not want a package to be grouped with others, we need to set its groupName
|
||||||
|
// to `null` after any other rule set it to something.
|
||||||
packageRules: [
|
packageRules: [
|
||||||
{
|
{
|
||||||
// Ignore major version bumps for these node packages
|
// Ignore major version bumps for these node packages
|
||||||
matchManagers: ['npm'],
|
matchManagers: ['npm'],
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
'@rails/ujs', // Needs to match the major Rails version
|
|
||||||
'tesseract.js', // Requires code changes
|
'tesseract.js', // Requires code changes
|
||||||
'react-hotkeys', // Requires code changes
|
'react-hotkeys', // Requires code changes
|
||||||
|
|
||||||
|
@ -46,17 +47,12 @@
|
||||||
// Ignore major version bumps for these Ruby packages
|
// Ignore major version bumps for these Ruby packages
|
||||||
matchManagers: ['bundler'],
|
matchManagers: ['bundler'],
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
|
'rack', // Needs to be synced with Rails version
|
||||||
'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
|
'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
|
||||||
'strong_migrations', // Requires manual upgrade
|
'strong_migrations', // Requires manual upgrade
|
||||||
'sidekiq', // Requires manual upgrade
|
'sidekiq', // Requires manual upgrade
|
||||||
'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version
|
'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version
|
||||||
'redis', // Requires manual upgrade and sync with Sidekiq version
|
'redis', // Requires manual upgrade and sync with Sidekiq version
|
||||||
'fog-openstack', // TODO: was ignored in https://github.com/mastodon/mastodon/pull/13964
|
|
||||||
|
|
||||||
// Needs major Rails version bump
|
|
||||||
'rack',
|
|
||||||
'rails',
|
|
||||||
'rails-i18n',
|
|
||||||
],
|
],
|
||||||
matchUpdateTypes: ['major'],
|
matchUpdateTypes: ['major'],
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
@ -91,12 +87,17 @@
|
||||||
// Update devDependencies every week, with one grouped PR
|
// Update devDependencies every week, with one grouped PR
|
||||||
matchDepTypes: 'devDependencies',
|
matchDepTypes: 'devDependencies',
|
||||||
matchUpdateTypes: ['patch', 'minor'],
|
matchUpdateTypes: ['patch', 'minor'],
|
||||||
excludePackageNames: [
|
|
||||||
'typescript', // Typescript has many changes in minor versions, needs to be checked every time
|
|
||||||
],
|
|
||||||
groupName: 'devDependencies (non-major)',
|
groupName: 'devDependencies (non-major)',
|
||||||
extends: ['schedule:weekly'],
|
extends: ['schedule:weekly'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Group all eslint-related packages with `eslint` in the same PR
|
||||||
|
matchManagers: ['npm'],
|
||||||
|
matchPackageNames: ['eslint'],
|
||||||
|
matchPackagePrefixes: ['eslint-', '@typescript-eslint/'],
|
||||||
|
matchUpdateTypes: ['patch', 'minor'],
|
||||||
|
groupName: 'eslint (non-major)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// Update @types/* packages every week, with one grouped PR
|
// Update @types/* packages every week, with one grouped PR
|
||||||
matchPackagePrefixes: '@types/',
|
matchPackagePrefixes: '@types/',
|
||||||
|
@ -105,6 +106,14 @@
|
||||||
extends: ['schedule:weekly'],
|
extends: ['schedule:weekly'],
|
||||||
addLabels: ['typescript'],
|
addLabels: ['typescript'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// We want those packages to always have their own PR
|
||||||
|
matchManagers: ['npm'],
|
||||||
|
matchPackageNames: [
|
||||||
|
'typescript', // Typescript has code-impacting changes in minor versions
|
||||||
|
],
|
||||||
|
groupName: null, // We dont want them to belong to any group
|
||||||
|
},
|
||||||
// Add labels depending on package manager
|
// Add labels depending on package manager
|
||||||
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
|
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
|
||||||
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
|
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
|
||||||
|
|
94
.github/workflows/build-container-image.yml
vendored
Normal file
94
.github/workflows/build-container-image.yml
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
platforms:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
use_native_arm64_builder:
|
||||||
|
type: boolean
|
||||||
|
push_to_images:
|
||||||
|
type: string
|
||||||
|
version_suffix:
|
||||||
|
type: string
|
||||||
|
flavor:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
type: string
|
||||||
|
labels:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: docker/setup-qemu-action@v2
|
||||||
|
if: contains(inputs.platforms, 'linux/arm64') && !inputs.use_native_arm64_builder
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@v2
|
||||||
|
id: buildx
|
||||||
|
if: ${{ !(inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')) }}
|
||||||
|
|
||||||
|
- name: Start a local Docker Builder
|
||||||
|
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
|
||||||
|
run: |
|
||||||
|
docker run --rm -d --name buildkitd -p 1234:1234 --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@v2
|
||||||
|
id: buildx-native
|
||||||
|
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
|
||||||
|
with:
|
||||||
|
driver: remote
|
||||||
|
endpoint: tcp://localhost:1234
|
||||||
|
platforms: linux/amd64
|
||||||
|
append: |
|
||||||
|
- endpoint: tcp://${{ vars.DOCKER_BUILDER_HETZNER_ARM64_01_HOST }}:13865
|
||||||
|
platforms: linux/arm64
|
||||||
|
name: mastodon-docker-builder-arm64-01
|
||||||
|
driver-opts:
|
||||||
|
- servername=mastodon-docker-builder-arm64-01
|
||||||
|
env:
|
||||||
|
BUILDER_NODE_1_AUTH_TLS_CACERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CACERT }}
|
||||||
|
BUILDER_NODE_1_AUTH_TLS_CERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CERT }}
|
||||||
|
BUILDER_NODE_1_AUTH_TLS_KEY: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_KEY }}
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
if: contains(inputs.push_to_images, 'tootsuite')
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to the Github Container registry
|
||||||
|
if: contains(inputs.push_to_images, 'ghcr.io')
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: docker/metadata-action@v4
|
||||||
|
id: meta
|
||||||
|
if: ${{ inputs.push_to_images != '' }}
|
||||||
|
with:
|
||||||
|
images: ${{ inputs.push_to_images }}
|
||||||
|
# Only tag with latest when ran against the latest stable branch
|
||||||
|
# This needs to be updated after each minor version release
|
||||||
|
flavor: ${{ inputs.flavor }}
|
||||||
|
tags: ${{ inputs.tags }}
|
||||||
|
labels: ${{ inputs.labels }}
|
||||||
|
|
||||||
|
- uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
build-args: MASTODON_VERSION_SUFFIX=${{ inputs.version_suffix }}
|
||||||
|
platforms: ${{ inputs.platforms }}
|
||||||
|
provenance: false
|
||||||
|
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }}
|
||||||
|
push: ${{ inputs.push_to_images != '' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
63
.github/workflows/build-image.yml
vendored
63
.github/workflows/build-image.yml
vendored
|
@ -1,63 +0,0 @@
|
||||||
name: Build container image
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/build-image.yml
|
|
||||||
- Dockerfile
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: hadolint/hadolint-action@v3.1.0
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Log in to the Github Container registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
|
|
||||||
- uses: docker/metadata-action@v4
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository_owner }}/mastodon
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
|
||||||
type=edge,branch=main
|
|
||||||
type=sha,prefix=,format=long
|
|
||||||
|
|
||||||
- name: Generate version suffix
|
|
||||||
id: version_vars
|
|
||||||
if: github.repository == 'mastodon/mastodon' && github.event_name == 'push' && github.ref_name == 'main'
|
|
||||||
run: |
|
|
||||||
echo mastodon_version_suffix=+edge-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: MASTODON_VERSION_SUFFIX=${{ steps.version_vars.outputs.mastodon_version_suffix }}
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
provenance: false
|
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
73
.github/workflows/build-nightly.yml
vendored
73
.github/workflows/build-nightly.yml
vendored
|
@ -3,58 +3,39 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-nightly-image:
|
compute-suffix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- id: version_vars
|
||||||
- uses: hadolint/hadolint-action@v3.1.0
|
env:
|
||||||
- uses: docker/setup-qemu-action@v2
|
TZ: Etc/UTC
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Log in to the Github Container registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- uses: docker/metadata-action@v4
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
ghcr.io/mastodon/mastodon
|
|
||||||
flavor: |
|
|
||||||
latest=auto
|
|
||||||
tags: |
|
|
||||||
type=raw,value=nightly
|
|
||||||
type=schedule,pattern=nightly-{{date 'YYYY-MM-DD' tz='Etc/UTC'}}
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.description=Nightly build image used for testing purposes
|
|
||||||
|
|
||||||
- name: Generate version suffix
|
|
||||||
id: version_vars
|
|
||||||
run: |
|
run: |
|
||||||
echo mastodon_version_suffix=+nightly-$(date +'%Y%m%d') >> $GITHUB_OUTPUT
|
echo mastodon_version_suffix=nightly-$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }}
|
||||||
|
|
||||||
- uses: docker/build-push-action@v4
|
build-image:
|
||||||
with:
|
needs: compute-suffix
|
||||||
context: .
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
build-args: MASTODON_VERSION_SUFFIX=${{ steps.version_vars.outputs.mastodon_version_suffix }}
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
provenance: false
|
use_native_arm64_builder: false
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
push_to_images: |
|
||||||
push: ${{ github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request' }}
|
ghcr.io/${{ github.repository_owner }}/mastodon
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
# The `+` is important here, result will be v4.1.2+nightly-2022-03-05
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
version_suffix: +${{ needs.compute-suffix.outputs.suffix }}
|
||||||
cache-from: type=gha
|
labels: |
|
||||||
cache-to: type=gha,mode=max
|
org.opencontainers.image.description=Nightly build image used for testing purposes
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
tags: |
|
||||||
|
type=raw,value=edge
|
||||||
|
type=raw,value=nightly
|
||||||
|
type=schedule,pattern=${{ needs.compute-suffix.outputs.suffix }}
|
||||||
|
secrets: inherit
|
||||||
|
|
41
.github/workflows/build-push-pr.yml
vendored
Normal file
41
.github/workflows/build-push-pr.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
name: Build container image for PR
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled, synchronize, reopened, ready_for_review, opened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
compute-suffix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# This is only allowed to run if:
|
||||||
|
# - the PR branch is in the `mastodon/mastodon` repository
|
||||||
|
# - the PR is not a draft
|
||||||
|
# - the PR has the "build-image" label
|
||||||
|
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft && contains(github.event.pull_request.labels.*.name, 'build-image') }}
|
||||||
|
steps:
|
||||||
|
# Repository needs to be cloned so `git rev-parse` below works
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- id: version_vars
|
||||||
|
run: |
|
||||||
|
echo mastodon_version_suffix=+pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }}
|
||||||
|
|
||||||
|
build-image:
|
||||||
|
needs: compute-suffix
|
||||||
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
use_native_arm64_builder: false
|
||||||
|
push_to_images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/mastodon
|
||||||
|
version_suffix: ${{ needs.compute-suffix.outputs.suffix }}
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
tags: |
|
||||||
|
type=ref,event=pr
|
||||||
|
secrets: inherit
|
24
.github/workflows/build-releases.yml
vendored
Normal file
24
.github/workflows/build-releases.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
name: Build container release images
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-image:
|
||||||
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
use_native_arm64_builder: false
|
||||||
|
push_to_images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/mastodon
|
||||||
|
flavor: |
|
||||||
|
latest=${{ startsWith(github.ref, 'refs/tags/v4.1.') }}
|
||||||
|
tags: |
|
||||||
|
type=pep440,pattern={{raw}}
|
||||||
|
type=pep440,pattern=v{{major}}.{{minor}}
|
||||||
|
secrets: inherit
|
40
.github/workflows/bundler-audit.yml
vendored
Normal file
40
.github/workflows/bundler-audit.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: Bundler Audit
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'dependabot/**'
|
||||||
|
paths:
|
||||||
|
- 'Gemfile*'
|
||||||
|
- '.ruby-version'
|
||||||
|
- '.bundler-audit.yml'
|
||||||
|
- '.github/workflows/bundler-audit.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'Gemfile*'
|
||||||
|
- '.ruby-version'
|
||||||
|
- '.bundler-audit.yml'
|
||||||
|
- '.github/workflows/bundler-audit.yml'
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: '0 5 * * 1'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install native Ruby dependencies
|
||||||
|
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Run bundler-audit
|
||||||
|
run: bundle exec bundler-audit
|
77
.github/workflows/crowdin-download.yml
vendored
Normal file
77
.github/workflows/crowdin-download.yml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
name: Crowdin / Download translations
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '17 4 * * *' # Every day
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
download-translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Increase Git http.postBuffer
|
||||||
|
# This is needed due to a bug in Ubuntu's cURL version?
|
||||||
|
# See https://github.com/orgs/community/discussions/55820
|
||||||
|
run: |
|
||||||
|
git config --global http.version HTTP/1.1
|
||||||
|
git config --global http.postBuffer 157286400
|
||||||
|
|
||||||
|
# Download the translation files from Crowdin
|
||||||
|
- name: crowdin action
|
||||||
|
uses: crowdin/github-action@v1
|
||||||
|
with:
|
||||||
|
config: crowdin-glitch.yml
|
||||||
|
upload_sources: false
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: true
|
||||||
|
crowdin_branch_name: main
|
||||||
|
push_translations: false
|
||||||
|
create_pull_request: false
|
||||||
|
env:
|
||||||
|
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
|
|
||||||
|
# As the files are extracted from a Docker container, they belong to root:root
|
||||||
|
# We need to fix this before the next steps
|
||||||
|
- name: Fix file permissions
|
||||||
|
run: sudo chown -R runner:docker .
|
||||||
|
|
||||||
|
# This is needed to run the normalize step
|
||||||
|
- name: Install native Ruby dependencies
|
||||||
|
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Run i18n normalize task
|
||||||
|
run: bundle exec i18n-tasks normalize
|
||||||
|
|
||||||
|
# Create or update the pull request
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5.0.2
|
||||||
|
with:
|
||||||
|
commit-message: 'New Crowdin translations'
|
||||||
|
title: 'New Crowdin Translations (automated)'
|
||||||
|
author: 'GitHub Actions <noreply@github.com>'
|
||||||
|
body: |
|
||||||
|
New Crowdin translations, automated with Github Actions
|
||||||
|
|
||||||
|
See `.github/workflows/crowdin-download.yml`
|
||||||
|
|
||||||
|
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.
|
||||||
|
If you want to run the checks, then close and re-open it.
|
||||||
|
branch: i18n/crowdin/translations
|
||||||
|
base: main
|
||||||
|
labels: i18n
|
36
.github/workflows/crowdin-upload.yml
vendored
Normal file
36
.github/workflows/crowdin-upload.yml
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
name: Crowdin / Upload translations
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- crowdin.yml
|
||||||
|
- app/javascript/mastodon/locales/en.json
|
||||||
|
- config/locales/en.yml
|
||||||
|
- config/locales/simple_form.en.yml
|
||||||
|
- config/locales/activerecord.en.yml
|
||||||
|
- config/locales/devise.en.yml
|
||||||
|
- config/locales/doorkeeper.en.yml
|
||||||
|
- .github/workflows/crowdin-upload.yml
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload-translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: crowdin action
|
||||||
|
uses: crowdin/github-action@v1
|
||||||
|
with:
|
||||||
|
config: crowdin-glitch.yml
|
||||||
|
upload_sources: true
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: false
|
||||||
|
crowdin_branch_name: main
|
||||||
|
|
||||||
|
env:
|
||||||
|
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
9
.github/workflows/lint-ruby.yml
vendored
9
.github/workflows/lint-ruby.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
||||||
- 'Gemfile*'
|
- 'Gemfile*'
|
||||||
- '.rubocop*.yml'
|
- '.rubocop*.yml'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
- '.bundler-audit.yml'
|
- 'config/brakeman.ignore'
|
||||||
- '**/*.rb'
|
- '**/*.rb'
|
||||||
- '**/*.rake'
|
- '**/*.rake'
|
||||||
- '.github/workflows/lint-ruby.yml'
|
- '.github/workflows/lint-ruby.yml'
|
||||||
|
@ -18,7 +18,7 @@ on:
|
||||||
- 'Gemfile*'
|
- 'Gemfile*'
|
||||||
- '.rubocop*.yml'
|
- '.rubocop*.yml'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
- '.bundler-audit.yml'
|
- 'config/brakeman.ignore'
|
||||||
- '**/*.rb'
|
- '**/*.rb'
|
||||||
- '**/*.rake'
|
- '**/*.rake'
|
||||||
- '.github/workflows/lint-ruby.yml'
|
- '.github/workflows/lint-ruby.yml'
|
||||||
|
@ -46,5 +46,6 @@ jobs:
|
||||||
- name: Run rubocop
|
- name: Run rubocop
|
||||||
run: bundle exec rubocop
|
run: bundle exec rubocop
|
||||||
|
|
||||||
- name: Run bundler-audit
|
- name: Run brakeman
|
||||||
run: bundle exec bundler-audit
|
if: always() # Run both checks, even if the first failed
|
||||||
|
run: bundle exec brakeman
|
||||||
|
|
15
.github/workflows/rebase-needed.yml
vendored
15
.github/workflows/rebase-needed.yml
vendored
|
@ -1,17 +1,8 @@
|
||||||
name: PR Needs Rebase
|
name: PR Needs Rebase
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
schedule:
|
||||||
branches-ignore:
|
- cron: '0 * * * *'
|
||||||
- 'dependabot/**'
|
|
||||||
- 'renovate/**'
|
|
||||||
- 'l10n_main'
|
|
||||||
pull_request_target:
|
|
||||||
branches-ignore:
|
|
||||||
- 'dependabot/**'
|
|
||||||
- 'renovate/**'
|
|
||||||
- 'l10n_main'
|
|
||||||
types: [synchronize]
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
@ -32,5 +23,5 @@ jobs:
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
commentOnClean: This pull request has resolved merge conflicts and is ready for review.
|
commentOnClean: This pull request has resolved merge conflicts and is ready for review.
|
||||||
commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged.
|
commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged.
|
||||||
retryMax: 10
|
retryMax: 30
|
||||||
continueOnMissingPermissions: false
|
continueOnMissingPermissions: false
|
||||||
|
|
21
.github/workflows/test-image-build.yml
vendored
Normal file
21
.github/workflows/test-image-build.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: Test container image build
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-nightly.yml
|
||||||
|
- .github/workflows/build-push-pr.yml
|
||||||
|
- .github/workflows/build-releases.yml
|
||||||
|
- .github/workflows/test-image-build.yml
|
||||||
|
- Dockerfile
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-image:
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64 # Testing only on native platform so it is performant
|
101
.github/workflows/test-ruby.yml
vendored
101
.github/workflows/test-ruby.yml
vendored
|
@ -107,6 +107,10 @@ jobs:
|
||||||
PAM_ENABLED: true
|
PAM_ENABLED: true
|
||||||
PAM_DEFAULT_SERVICE: pam_test
|
PAM_DEFAULT_SERVICE: pam_test
|
||||||
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||||
|
OIDC_ENABLED: true
|
||||||
|
OIDC_SCOPE: read
|
||||||
|
SAML_ENABLED: true
|
||||||
|
CAS_ENABLED: true
|
||||||
BUNDLE_WITH: 'pam_authentication test'
|
BUNDLE_WITH: 'pam_authentication test'
|
||||||
CI_JOBS: ${{ matrix.ci_job }}/4
|
CI_JOBS: ${{ matrix.ci_job }}/4
|
||||||
|
|
||||||
|
@ -149,3 +153,100 @@ jobs:
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
|
||||||
- run: bundle exec rake rspec_chunked
|
- run: bundle exec rake rspec_chunked
|
||||||
|
|
||||||
|
test-e2e:
|
||||||
|
name: End to End testing
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:14-alpine
|
||||||
|
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:
|
||||||
|
DB_HOST: localhost
|
||||||
|
DB_USER: postgres
|
||||||
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: true
|
||||||
|
RAILS_ENV: test
|
||||||
|
BUNDLE_WITH: test
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
ruby-version:
|
||||||
|
- '3.0'
|
||||||
|
- '3.1'
|
||||||
|
- '.ruby-version'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: './public'
|
||||||
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Update package index
|
||||||
|
run: sudo apt-get update
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
cache: yarn
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
|
||||||
|
- name: Install native Ruby dependencies
|
||||||
|
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||||
|
|
||||||
|
- name: Install additional system dependencies
|
||||||
|
run: sudo apt-get install -y ffmpeg imagemagick
|
||||||
|
|
||||||
|
- name: Set up bundler cache
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- run: yarn --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Load database schema
|
||||||
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
|
||||||
|
- run: bundle exec rake spec:system
|
||||||
|
|
||||||
|
- name: Archive logs
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: e2e-logs-${{ matrix.ruby-version }}
|
||||||
|
path: log/
|
||||||
|
|
||||||
|
- name: Archive test screenshots
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: e2e-screenshots
|
||||||
|
path: tmp/screenshots/
|
||||||
|
|
|
@ -1,73 +1,23 @@
|
||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `haml-lint --auto-gen-config`
|
# `haml-lint --auto-gen-config`
|
||||||
# on 2023-03-15 00:55:01 -0400 using Haml-Lint version 0.45.0.
|
# on 2023-07-20 09:47:50 -0400 using Haml-Lint version 0.48.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the lints are removed from the code base.
|
# one by one as the lints are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of Haml-Lint, may require this file to be generated again.
|
# versions of Haml-Lint, may require this file to be generated again.
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
# Offense count: 63
|
# Offense count: 951
|
||||||
RuboCop:
|
|
||||||
exclude:
|
|
||||||
- 'app/views/accounts/_og.html.haml'
|
|
||||||
- 'app/views/admin/account_warnings/_account_warning.html.haml'
|
|
||||||
- 'app/views/admin/accounts/index.html.haml'
|
|
||||||
- 'app/views/admin/accounts/show.html.haml'
|
|
||||||
- 'app/views/admin/announcements/edit.html.haml'
|
|
||||||
- 'app/views/admin/announcements/new.html.haml'
|
|
||||||
- 'app/views/admin/disputes/appeals/_appeal.html.haml'
|
|
||||||
- 'app/views/admin/domain_blocks/edit.html.haml'
|
|
||||||
- 'app/views/admin/domain_blocks/new.html.haml'
|
|
||||||
- 'app/views/admin/ip_blocks/new.html.haml'
|
|
||||||
- 'app/views/admin/reports/actions/preview.html.haml'
|
|
||||||
- 'app/views/admin/reports/index.html.haml'
|
|
||||||
- 'app/views/admin/reports/show.html.haml'
|
|
||||||
- 'app/views/admin/roles/_form.html.haml'
|
|
||||||
- 'app/views/admin/settings/about/show.html.haml'
|
|
||||||
- 'app/views/admin/settings/appearance/show.html.haml'
|
|
||||||
- 'app/views/admin/settings/registrations/show.html.haml'
|
|
||||||
- 'app/views/admin/statuses/show.html.haml'
|
|
||||||
- 'app/views/auth/registrations/new.html.haml'
|
|
||||||
- 'app/views/disputes/strikes/show.html.haml'
|
|
||||||
- 'app/views/filters/_filter_fields.html.haml'
|
|
||||||
- 'app/views/invites/_form.html.haml'
|
|
||||||
- 'app/views/layouts/application.html.haml'
|
|
||||||
- 'app/views/layouts/error.html.haml'
|
|
||||||
- 'app/views/notification_mailer/_status.html.haml'
|
|
||||||
- 'app/views/settings/applications/_fields.html.haml'
|
|
||||||
- 'app/views/settings/imports/show.html.haml'
|
|
||||||
- 'app/views/settings/preferences/appearance/show.html.haml'
|
|
||||||
- 'app/views/settings/preferences/other/show.html.haml'
|
|
||||||
- 'app/views/statuses/_detailed_status.html.haml'
|
|
||||||
- 'app/views/statuses/_poll.html.haml'
|
|
||||||
- 'app/views/statuses/show.html.haml'
|
|
||||||
- 'app/views/statuses_cleanup/show.html.haml'
|
|
||||||
- 'app/views/user_mailer/warning.html.haml'
|
|
||||||
|
|
||||||
# Offense count: 913
|
|
||||||
LineLength:
|
LineLength:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# Offense count: 22
|
# Offense count: 22
|
||||||
UnnecessaryStringOutput:
|
UnnecessaryStringOutput:
|
||||||
exclude:
|
enabled: false
|
||||||
- 'app/views/accounts/show.html.haml'
|
|
||||||
- 'app/views/admin/custom_emojis/_custom_emoji.html.haml'
|
# Offense count: 57
|
||||||
- 'app/views/admin/relays/_relay.html.haml'
|
RuboCop:
|
||||||
- 'app/views/admin/rules/_rule.html.haml'
|
enabled: false
|
||||||
- 'app/views/admin/statuses/index.html.haml'
|
|
||||||
- 'app/views/auth/registrations/_sessions.html.haml'
|
|
||||||
- 'app/views/disputes/strikes/show.html.haml'
|
|
||||||
- 'app/views/notification_mailer/_status.html.haml'
|
|
||||||
- 'app/views/settings/two_factor_authentication_methods/index.html.haml'
|
|
||||||
- 'app/views/statuses/_detailed_status.html.haml'
|
|
||||||
- 'app/views/statuses/_poll.html.haml'
|
|
||||||
- 'app/views/statuses/_simple_status.html.haml'
|
|
||||||
- 'app/views/user_mailer/suspicious_sign_in.html.haml'
|
|
||||||
- 'app/views/user_mailer/webauthn_credential_added.html.haml'
|
|
||||||
- 'app/views/user_mailer/webauthn_credential_deleted.html.haml'
|
|
||||||
- 'app/views/user_mailer/welcome.html.haml'
|
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
ViewLength:
|
ViewLength:
|
||||||
|
@ -76,27 +26,18 @@ linters:
|
||||||
- 'app/views/admin/reports/show.html.haml'
|
- 'app/views/admin/reports/show.html.haml'
|
||||||
- 'app/views/disputes/strikes/show.html.haml'
|
- 'app/views/disputes/strikes/show.html.haml'
|
||||||
|
|
||||||
# Offense count: 41
|
# Offense count: 32
|
||||||
InstanceVariables:
|
InstanceVariables:
|
||||||
exclude:
|
exclude:
|
||||||
- 'app/views/admin/reports/_actions.html.haml'
|
- 'app/views/admin/reports/_actions.html.haml'
|
||||||
- 'app/views/admin/roles/_form.html.haml'
|
- 'app/views/admin/roles/_form.html.haml'
|
||||||
- 'app/views/admin/webhooks/_form.html.haml'
|
- 'app/views/admin/webhooks/_form.html.haml'
|
||||||
- 'app/views/auth/registrations/_sessions.html.haml'
|
|
||||||
- 'app/views/auth/registrations/_status.html.haml'
|
- 'app/views/auth/registrations/_status.html.haml'
|
||||||
- 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml'
|
- 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml'
|
||||||
- 'app/views/authorize_interactions/_post_follow_actions.html.haml'
|
- 'app/views/authorize_interactions/_post_follow_actions.html.haml'
|
||||||
- 'app/views/invites/_form.html.haml'
|
- 'app/views/invites/_form.html.haml'
|
||||||
- 'app/views/relationships/_account.html.haml'
|
- 'app/views/relationships/_account.html.haml'
|
||||||
- 'app/views/shared/_og.html.haml'
|
- 'app/views/shared/_og.html.haml'
|
||||||
- 'app/views/statuses/_status.html.haml'
|
|
||||||
|
|
||||||
# Offense count: 6
|
|
||||||
ConsecutiveSilentScripts:
|
|
||||||
exclude:
|
|
||||||
- 'app/views/admin/settings/shared/_links.html.haml'
|
|
||||||
- 'app/views/settings/login_activities/_login_activity.html.haml'
|
|
||||||
- 'app/views/statuses/_poll.html.haml'
|
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
IdNames:
|
IdNames:
|
||||||
|
|
21
.rubocop.yml
21
.rubocop.yml
|
@ -24,7 +24,6 @@ AllCops:
|
||||||
Exclude:
|
Exclude:
|
||||||
- db/schema.rb
|
- db/schema.rb
|
||||||
- 'bin/*'
|
- 'bin/*'
|
||||||
- 'Rakefile'
|
|
||||||
- 'node_modules/**/*'
|
- 'node_modules/**/*'
|
||||||
- 'Vagrantfile'
|
- 'Vagrantfile'
|
||||||
- 'vendor/**/*'
|
- 'vendor/**/*'
|
||||||
|
@ -39,14 +38,7 @@ Layout/FirstHashElementIndentation:
|
||||||
# Reason: Currently disabled in .rubocop_todo.yml
|
# Reason: Currently disabled in .rubocop_todo.yml
|
||||||
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
|
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
|
||||||
Layout/LineLength:
|
Layout/LineLength:
|
||||||
AllowedPatterns:
|
Max: 320 # Default of 120 causes a duplicate entry in generated todo file
|
||||||
# Allow comments to be long lines
|
|
||||||
- !ruby/regexp / \# .*$/
|
|
||||||
- !ruby/regexp /^\# .*$/
|
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/cli/*.rb'
|
|
||||||
- db/*migrate/**/*
|
|
||||||
- db/seeds/**/*
|
|
||||||
|
|
||||||
# Reason:
|
# Reason:
|
||||||
# https://docs.rubocop.org/rubocop/cops_lint.html#lintuselessaccessmodifier
|
# https://docs.rubocop.org/rubocop/cops_lint.html#lintuselessaccessmodifier
|
||||||
|
@ -132,12 +124,6 @@ RSpec/FilePath:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/config/initializers/rack_attack_spec.rb' # namespaces usually have separate folder
|
- 'spec/config/initializers/rack_attack_spec.rb' # namespaces usually have separate folder
|
||||||
- 'spec/lib/sanitize_config_spec.rb' # namespaces usually have separate folder
|
- 'spec/lib/sanitize_config_spec.rb' # namespaces usually have separate folder
|
||||||
- 'spec/controllers/concerns/account_controller_concern_spec.rb' # Concerns describe ApplicationController and don't fit naming
|
|
||||||
- 'spec/controllers/concerns/export_controller_concern_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/localized_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/rate_limit_headers_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/signature_verification_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/user_tracking_concern_spec.rb'
|
|
||||||
|
|
||||||
# Reason:
|
# Reason:
|
||||||
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
|
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
|
||||||
|
@ -192,6 +178,11 @@ Style/RedundantBegin:
|
||||||
Style/RescueStandardError:
|
Style/RescueStandardError:
|
||||||
EnforcedStyle: implicit
|
EnforcedStyle: implicit
|
||||||
|
|
||||||
|
# Reason: Simplify some spec layouts
|
||||||
|
# https://docs.rubocop.org/rubocop/cops_style.html#stylesemicolon
|
||||||
|
Style/Semicolon:
|
||||||
|
AllowAsExpressionSeparator: true
|
||||||
|
|
||||||
# Reason: Originally disabled for CodeClimate, and no config consensus has been found
|
# Reason: Originally disabled for CodeClimate, and no config consensus has been found
|
||||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
|
# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
|
||||||
Style/SymbolArray:
|
Style/SymbolArray:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||||
# using RuboCop version 1.52.1.
|
# using RuboCop version 1.54.2.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
|
@ -28,7 +28,6 @@ Layout/ArgumentAlignment:
|
||||||
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
||||||
Layout/HashAlignment:
|
Layout/HashAlignment:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/boot.rb'
|
|
||||||
- 'config/environments/production.rb'
|
- 'config/environments/production.rb'
|
||||||
- 'config/initializers/rack_attack.rb'
|
- 'config/initializers/rack_attack.rb'
|
||||||
- 'config/routes.rb'
|
- 'config/routes.rb'
|
||||||
|
@ -40,6 +39,13 @@ Layout/LeadingCommentSpace:
|
||||||
- 'config/application.rb'
|
- 'config/application.rb'
|
||||||
- 'config/initializers/omniauth.rb'
|
- 'config/initializers/omniauth.rb'
|
||||||
|
|
||||||
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
||||||
|
# URISchemes: http, https
|
||||||
|
Layout/LineLength:
|
||||||
|
Exclude:
|
||||||
|
- 'app/models/account.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: require_no_space, require_space
|
# SupportedStyles: require_no_space, require_space
|
||||||
|
@ -48,15 +54,6 @@ Layout/SpaceInLambdaLiteral:
|
||||||
- 'config/environments/production.rb'
|
- 'config/environments/production.rb'
|
||||||
- 'config/initializers/content_security_policy.rb'
|
- 'config/initializers/content_security_policy.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
||||||
Lint/AmbiguousBlockAssociation:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
|
|
||||||
- 'spec/controllers/settings/two_factor_authentication/otp_authentication_controller_spec.rb'
|
|
||||||
- 'spec/services/activitypub/process_status_update_service_spec.rb'
|
|
||||||
- 'spec/services/post_status_service_spec.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
||||||
Lint/EmptyBlock:
|
Lint/EmptyBlock:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -106,11 +103,6 @@ Lint/OrAssignmentToConstant:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/sanitize_ext/sanitize_config.rb'
|
- 'lib/sanitize_ext/sanitize_config.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
Lint/SendWithMixinArgument:
|
|
||||||
Exclude:
|
|
||||||
- 'config/application.rb'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
||||||
Lint/UnusedBlockArgument:
|
Lint/UnusedBlockArgument:
|
||||||
|
@ -127,7 +119,6 @@ Lint/UselessAssignment:
|
||||||
- 'config/initializers/omniauth.rb'
|
- 'config/initializers/omniauth.rb'
|
||||||
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
|
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
|
||||||
- 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
|
- 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
|
||||||
- 'spec/controllers/api/v1/bookmarks_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/favourites_controller_spec.rb'
|
- 'spec/controllers/api/v1/favourites_controller_spec.rb'
|
||||||
- 'spec/controllers/concerns/account_controller_concern_spec.rb'
|
- 'spec/controllers/concerns/account_controller_concern_spec.rb'
|
||||||
- 'spec/helpers/jsonld_helper_spec.rb'
|
- 'spec/helpers/jsonld_helper_spec.rb'
|
||||||
|
@ -142,15 +133,9 @@ Lint/UselessAssignment:
|
||||||
- 'spec/services/resolve_url_service_spec.rb'
|
- 'spec/services/resolve_url_service_spec.rb'
|
||||||
- 'spec/views/statuses/show.html.haml_spec.rb'
|
- 'spec/views/statuses/show.html.haml_spec.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: CheckForMethodsWithNoSideEffects.
|
|
||||||
Lint/Void:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/services/resolve_account_service_spec.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 150
|
Max: 143
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/serializers/initial_state_serializer.rb'
|
- 'app/serializers/initial_state_serializer.rb'
|
||||||
|
|
||||||
|
@ -167,30 +152,6 @@ Metrics/CyclomaticComplexity:
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 27
|
Max: 27
|
||||||
|
|
||||||
Naming/AccessorMethodName:
|
|
||||||
Exclude:
|
|
||||||
- 'app/controllers/auth/sessions_controller.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
|
|
||||||
# CheckDefinitionPathHierarchyRoots: lib, spec, test, src
|
|
||||||
# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
|
|
||||||
Naming/FileName:
|
|
||||||
Exclude:
|
|
||||||
- 'config/locales/sr-Latn.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
|
|
||||||
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
|
|
||||||
Naming/MemoizedInstanceVariableName:
|
|
||||||
Exclude:
|
|
||||||
- 'app/controllers/api/v1/bookmarks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/favourites_controller.rb'
|
|
||||||
- 'app/controllers/concerns/rate_limit_headers.rb'
|
|
||||||
- 'app/lib/activitypub/activity.rb'
|
|
||||||
- 'app/services/resolve_url_service.rb'
|
|
||||||
- 'app/services/search_service.rb'
|
|
||||||
- 'config/initializers/rack_attack.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
||||||
# SupportedStyles: snake_case, normalcase, non_integer
|
# SupportedStyles: snake_case, normalcase, non_integer
|
||||||
# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
||||||
|
@ -205,14 +166,6 @@ Naming/VariableNumber:
|
||||||
- 'spec/models/domain_block_spec.rb'
|
- 'spec/models/domain_block_spec.rb'
|
||||||
- 'spec/models/user_spec.rb'
|
- 'spec/models/user_spec.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
Performance/UnfreezeString:
|
|
||||||
Exclude:
|
|
||||||
- 'app/lib/rss/builder.rb'
|
|
||||||
- 'app/lib/text_formatter.rb'
|
|
||||||
- 'app/validators/status_length_validator.rb'
|
|
||||||
- 'lib/tasks/mastodon.rake'
|
|
||||||
|
|
||||||
RSpec/AnyInstance:
|
RSpec/AnyInstance:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/controllers/activitypub/inboxes_controller_spec.rb'
|
- 'spec/controllers/activitypub/inboxes_controller_spec.rb'
|
||||||
|
@ -228,46 +181,10 @@ RSpec/AnyInstance:
|
||||||
- 'spec/models/account_spec.rb'
|
- 'spec/models/account_spec.rb'
|
||||||
- 'spec/models/setting_spec.rb'
|
- 'spec/models/setting_spec.rb'
|
||||||
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
||||||
- 'spec/validators/blacklisted_email_validator_spec.rb'
|
|
||||||
- 'spec/validators/follow_limit_validator_spec.rb'
|
- 'spec/validators/follow_limit_validator_spec.rb'
|
||||||
- 'spec/workers/activitypub/delivery_worker_spec.rb'
|
- 'spec/workers/activitypub/delivery_worker_spec.rb'
|
||||||
- 'spec/workers/web/push_notification_worker_spec.rb'
|
- 'spec/workers/web/push_notification_worker_spec.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
RSpec/EmptyExampleGroup:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/helpers/admin/action_logs_helper_spec.rb'
|
|
||||||
- 'spec/models/account_alias_spec.rb'
|
|
||||||
- 'spec/models/account_deletion_request_spec.rb'
|
|
||||||
- 'spec/models/account_moderation_note_spec.rb'
|
|
||||||
- 'spec/models/announcement_mute_spec.rb'
|
|
||||||
- 'spec/models/announcement_reaction_spec.rb'
|
|
||||||
- 'spec/models/announcement_spec.rb'
|
|
||||||
- 'spec/models/backup_spec.rb'
|
|
||||||
- 'spec/models/conversation_mute_spec.rb'
|
|
||||||
- 'spec/models/custom_filter_keyword_spec.rb'
|
|
||||||
- 'spec/models/custom_filter_spec.rb'
|
|
||||||
- 'spec/models/device_spec.rb'
|
|
||||||
- 'spec/models/encrypted_message_spec.rb'
|
|
||||||
- 'spec/models/featured_tag_spec.rb'
|
|
||||||
- 'spec/models/follow_recommendation_suppression_spec.rb'
|
|
||||||
- 'spec/models/list_account_spec.rb'
|
|
||||||
- 'spec/models/list_spec.rb'
|
|
||||||
- 'spec/models/login_activity_spec.rb'
|
|
||||||
- 'spec/models/mute_spec.rb'
|
|
||||||
- 'spec/models/preview_card_spec.rb'
|
|
||||||
- 'spec/models/preview_card_trend_spec.rb'
|
|
||||||
- 'spec/models/relay_spec.rb'
|
|
||||||
- 'spec/models/scheduled_status_spec.rb'
|
|
||||||
- 'spec/models/status_stat_spec.rb'
|
|
||||||
- 'spec/models/status_trend_spec.rb'
|
|
||||||
- 'spec/models/system_key_spec.rb'
|
|
||||||
- 'spec/models/tag_follow_spec.rb'
|
|
||||||
- 'spec/models/unavailable_domain_spec.rb'
|
|
||||||
- 'spec/models/user_invite_request_spec.rb'
|
|
||||||
- 'spec/models/web/setting_spec.rb'
|
|
||||||
- 'spec/services/unmute_service_spec.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: CountAsOne.
|
# Configuration parameters: CountAsOne.
|
||||||
RSpec/ExampleLength:
|
RSpec/ExampleLength:
|
||||||
Max: 22
|
Max: 22
|
||||||
|
@ -285,7 +202,6 @@ RSpec/HookArgument:
|
||||||
- 'spec/serializers/activitypub/note_serializer_spec.rb'
|
- 'spec/serializers/activitypub/note_serializer_spec.rb'
|
||||||
- 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
|
- 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
|
||||||
- 'spec/services/import_service_spec.rb'
|
- 'spec/services/import_service_spec.rb'
|
||||||
- 'spec/spec_helper.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: AssignmentOnly.
|
# Configuration parameters: AssignmentOnly.
|
||||||
RSpec/InstanceVariable:
|
RSpec/InstanceVariable:
|
||||||
|
@ -321,7 +237,6 @@ RSpec/LetSetup:
|
||||||
- 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
|
- 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
|
||||||
- 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
|
- 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
|
||||||
- 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
|
- 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
|
||||||
- 'spec/controllers/api/v2/filters_controller_spec.rb'
|
|
||||||
- 'spec/controllers/auth/confirmations_controller_spec.rb'
|
- 'spec/controllers/auth/confirmations_controller_spec.rb'
|
||||||
- 'spec/controllers/auth/passwords_controller_spec.rb'
|
- 'spec/controllers/auth/passwords_controller_spec.rb'
|
||||||
- 'spec/controllers/auth/sessions_controller_spec.rb'
|
- 'spec/controllers/auth/sessions_controller_spec.rb'
|
||||||
|
@ -331,6 +246,7 @@ RSpec/LetSetup:
|
||||||
- 'spec/controllers/oauth/tokens_controller_spec.rb'
|
- 'spec/controllers/oauth/tokens_controller_spec.rb'
|
||||||
- 'spec/controllers/settings/imports_controller_spec.rb'
|
- 'spec/controllers/settings/imports_controller_spec.rb'
|
||||||
- 'spec/lib/activitypub/activity/delete_spec.rb'
|
- 'spec/lib/activitypub/activity/delete_spec.rb'
|
||||||
|
- 'spec/lib/vacuum/applications_vacuum_spec.rb'
|
||||||
- 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
|
- 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
|
||||||
- 'spec/models/account_spec.rb'
|
- 'spec/models/account_spec.rb'
|
||||||
- 'spec/models/account_statuses_cleanup_policy_spec.rb'
|
- 'spec/models/account_statuses_cleanup_policy_spec.rb'
|
||||||
|
@ -368,11 +284,7 @@ RSpec/MessageChain:
|
||||||
RSpec/MessageSpies:
|
RSpec/MessageSpies:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/controllers/admin/accounts_controller_spec.rb'
|
- 'spec/controllers/admin/accounts_controller_spec.rb'
|
||||||
- 'spec/controllers/api/base_controller_spec.rb'
|
|
||||||
- 'spec/controllers/auth/registrations_controller_spec.rb'
|
|
||||||
- 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
|
- 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
|
||||||
- 'spec/helpers/application_helper_spec.rb'
|
|
||||||
- 'spec/lib/status_finder_spec.rb'
|
|
||||||
- 'spec/lib/webfinger_resource_spec.rb'
|
- 'spec/lib/webfinger_resource_spec.rb'
|
||||||
- 'spec/models/admin/account_action_spec.rb'
|
- 'spec/models/admin/account_action_spec.rb'
|
||||||
- 'spec/models/concerns/remotable_spec.rb'
|
- 'spec/models/concerns/remotable_spec.rb'
|
||||||
|
@ -400,89 +312,13 @@ RSpec/PendingWithoutReason:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/models/account_spec.rb'
|
- 'spec/models/account_spec.rb'
|
||||||
|
|
||||||
RSpec/StubbedMock:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/controllers/api/base_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/media_controller_spec.rb'
|
|
||||||
- 'spec/controllers/auth/registrations_controller_spec.rb'
|
|
||||||
- 'spec/helpers/application_helper_spec.rb'
|
|
||||||
- 'spec/lib/status_filter_spec.rb'
|
|
||||||
- 'spec/lib/status_finder_spec.rb'
|
|
||||||
- 'spec/lib/webfinger_resource_spec.rb'
|
|
||||||
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
|
||||||
|
|
||||||
RSpec/SubjectDeclaration:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/controllers/admin/domain_blocks_controller_spec.rb'
|
|
||||||
- 'spec/models/account_migration_spec.rb'
|
|
||||||
- 'spec/models/account_spec.rb'
|
|
||||||
- 'spec/models/relationship_filter_spec.rb'
|
|
||||||
- 'spec/models/user_role_spec.rb'
|
|
||||||
- 'spec/policies/account_moderation_note_policy_spec.rb'
|
|
||||||
- 'spec/policies/account_policy_spec.rb'
|
|
||||||
- 'spec/policies/backup_policy_spec.rb'
|
|
||||||
- 'spec/policies/custom_emoji_policy_spec.rb'
|
|
||||||
- 'spec/policies/domain_block_policy_spec.rb'
|
|
||||||
- 'spec/policies/email_domain_block_policy_spec.rb'
|
|
||||||
- 'spec/policies/instance_policy_spec.rb'
|
|
||||||
- 'spec/policies/invite_policy_spec.rb'
|
|
||||||
- 'spec/policies/relay_policy_spec.rb'
|
|
||||||
- 'spec/policies/report_note_policy_spec.rb'
|
|
||||||
- 'spec/policies/report_policy_spec.rb'
|
|
||||||
- 'spec/policies/settings_policy_spec.rb'
|
|
||||||
- 'spec/policies/tag_policy_spec.rb'
|
|
||||||
- 'spec/policies/user_policy_spec.rb'
|
|
||||||
- 'spec/services/activitypub/process_account_service_spec.rb'
|
|
||||||
|
|
||||||
RSpec/SubjectStub:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/services/unallow_domain_service_spec.rb'
|
|
||||||
- 'spec/validators/blacklisted_email_validator_spec.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
Rails/ApplicationController:
|
Rails/ApplicationController:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/health_controller.rb'
|
- 'app/controllers/health_controller.rb'
|
||||||
|
|
||||||
# Configuration parameters: Database, Include.
|
|
||||||
# SupportedDatabases: mysql, postgresql
|
|
||||||
# Include: db/migrate/*.rb
|
|
||||||
Rails/BulkChangeTable:
|
|
||||||
Exclude:
|
|
||||||
- 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160223162837_add_metadata_to_statuses.rb'
|
|
||||||
- 'db/migrate/20160305115639_add_devise_to_users.rb'
|
|
||||||
- 'db/migrate/20160314164231_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20160926213048_remove_owner_from_application.rb'
|
|
||||||
- 'db/migrate/20161003142332_add_confirmable_to_users.rb'
|
|
||||||
- 'db/migrate/20170112154826_migrate_settings.rb'
|
|
||||||
- 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb'
|
|
||||||
- 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
|
|
||||||
- 'db/migrate/20170330021336_add_counter_caches.rb'
|
|
||||||
- 'db/migrate/20170425202925_add_oembed_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20170427011934_re_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
|
|
||||||
- 'db/migrate/20170624134742_add_description_to_session_activations.rb'
|
|
||||||
- 'db/migrate/20170718211102_add_activitypub_to_accounts.rb'
|
|
||||||
- 'db/migrate/20171006142024_add_uri_to_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20180812123222_change_relays_enabled.rb'
|
|
||||||
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
|
|
||||||
- 'db/migrate/20190805123746_add_capabilities_to_tags.rb'
|
|
||||||
- 'db/migrate/20190807135426_add_comments_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20190815225426_add_last_status_at_to_tags.rb'
|
|
||||||
- 'db/migrate/20190901035623_add_max_score_to_tags.rb'
|
|
||||||
- 'db/migrate/20200417125749_add_storage_schema_version.rb'
|
|
||||||
- 'db/migrate/20200608113046_add_sign_in_token_to_users.rb'
|
|
||||||
- 'db/migrate/20211112011713_add_language_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20211231080958_add_category_to_reports.rb'
|
|
||||||
- 'db/migrate/20220202200743_add_trendable_to_accounts.rb'
|
|
||||||
- 'db/migrate/20220224010024_add_ips_to_email_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20220227041951_add_last_used_at_to_oauth_access_tokens.rb'
|
|
||||||
- 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb'
|
|
||||||
- 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb'
|
|
||||||
|
|
||||||
# Configuration parameters: Include.
|
# Configuration parameters: Include.
|
||||||
# Include: db/migrate/*.rb
|
# Include: db/**/*.rb
|
||||||
Rails/CreateTableWithTimestamps:
|
Rails/CreateTableWithTimestamps:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'db/migrate/20170508230434_create_conversation_mutes.rb'
|
- 'db/migrate/20170508230434_create_conversation_mutes.rb'
|
||||||
|
@ -756,7 +592,7 @@ Style/FetchEnvVar:
|
||||||
- 'app/lib/translation_service.rb'
|
- 'app/lib/translation_service.rb'
|
||||||
- 'config/environments/development.rb'
|
- 'config/environments/development.rb'
|
||||||
- 'config/environments/production.rb'
|
- 'config/environments/production.rb'
|
||||||
- 'config/initializers/2_whitelist_mode.rb'
|
- 'config/initializers/2_limited_federation_mode.rb'
|
||||||
- 'config/initializers/blacklists.rb'
|
- 'config/initializers/blacklists.rb'
|
||||||
- 'config/initializers/cache_buster.rb'
|
- 'config/initializers/cache_buster.rb'
|
||||||
- 'config/initializers/content_security_policy.rb'
|
- 'config/initializers/content_security_policy.rb'
|
||||||
|
@ -779,406 +615,6 @@ Style/FormatStringToken:
|
||||||
- 'config/initializers/devise.rb'
|
- 'config/initializers/devise.rb'
|
||||||
- 'lib/paperclip/color_extractor.rb'
|
- 'lib/paperclip/color_extractor.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: always, always_true, never
|
|
||||||
Style/FrozenStringLiteralComment:
|
|
||||||
Exclude:
|
|
||||||
- 'app/views/accounts/show.rss.ruby'
|
|
||||||
- 'app/views/tags/show.rss.ruby'
|
|
||||||
- 'app/views/well_known/host_meta/show.xml.ruby'
|
|
||||||
- 'config/application.rb'
|
|
||||||
- 'config/boot.rb'
|
|
||||||
- 'config/environment.rb'
|
|
||||||
- 'config/environments/development.rb'
|
|
||||||
- 'config/environments/production.rb'
|
|
||||||
- 'config/environments/test.rb'
|
|
||||||
- 'config/initializers/0_post_deployment_migrations.rb'
|
|
||||||
- 'config/initializers/active_model_serializers.rb'
|
|
||||||
- 'config/initializers/application_controller_renderer.rb'
|
|
||||||
- 'config/initializers/assets.rb'
|
|
||||||
- 'config/initializers/backtrace_silencers.rb'
|
|
||||||
- 'config/initializers/cache_logging.rb'
|
|
||||||
- 'config/initializers/chewy.rb'
|
|
||||||
- 'config/initializers/content_security_policy.rb'
|
|
||||||
- 'config/initializers/cookies_serializer.rb'
|
|
||||||
- 'config/initializers/cors.rb'
|
|
||||||
- 'config/initializers/devise.rb'
|
|
||||||
- 'config/initializers/doorkeeper.rb'
|
|
||||||
- 'config/initializers/fast_blank.rb'
|
|
||||||
- 'config/initializers/ffmpeg.rb'
|
|
||||||
- 'config/initializers/filter_parameter_logging.rb'
|
|
||||||
- 'config/initializers/http_client_proxy.rb'
|
|
||||||
- 'config/initializers/httplog.rb'
|
|
||||||
- 'config/initializers/inflections.rb'
|
|
||||||
- 'config/initializers/mail_delivery_job.rb'
|
|
||||||
- 'config/initializers/makara.rb'
|
|
||||||
- 'config/initializers/mime_types.rb'
|
|
||||||
- 'config/initializers/oj.rb'
|
|
||||||
- 'config/initializers/omniauth.rb'
|
|
||||||
- 'config/initializers/open_uri_redirection.rb'
|
|
||||||
- 'config/initializers/permissions_policy.rb'
|
|
||||||
- 'config/initializers/pghero.rb'
|
|
||||||
- 'config/initializers/preload_link_headers.rb'
|
|
||||||
- 'config/initializers/premailer_rails.rb'
|
|
||||||
- 'config/initializers/rack_attack_logging.rb'
|
|
||||||
- 'config/initializers/redis.rb'
|
|
||||||
- 'config/initializers/session_store.rb'
|
|
||||||
- 'config/initializers/simple_form.rb'
|
|
||||||
- 'config/initializers/stoplight.rb'
|
|
||||||
- 'config/initializers/trusted_proxies.rb'
|
|
||||||
- 'config/initializers/twitter_regex.rb'
|
|
||||||
- 'config/initializers/webauthn.rb'
|
|
||||||
- 'config/initializers/wrap_parameters.rb'
|
|
||||||
- 'config/locales/sr-Latn.rb'
|
|
||||||
- 'config/locales/sr.rb'
|
|
||||||
- 'config/puma.rb'
|
|
||||||
- 'db/migrate/20160220174730_create_accounts.rb'
|
|
||||||
- 'db/migrate/20160220211917_create_statuses.rb'
|
|
||||||
- 'db/migrate/20160221003140_create_users.rb'
|
|
||||||
- 'db/migrate/20160221003621_create_follows.rb'
|
|
||||||
- 'db/migrate/20160222122600_create_stream_entries.rb'
|
|
||||||
- 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160223162837_add_metadata_to_statuses.rb'
|
|
||||||
- 'db/migrate/20160223164502_make_uris_nullable_in_statuses.rb'
|
|
||||||
- 'db/migrate/20160223165723_add_url_to_statuses.rb'
|
|
||||||
- 'db/migrate/20160223165855_add_url_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160223171800_create_favourites.rb'
|
|
||||||
- 'db/migrate/20160224223247_create_mentions.rb'
|
|
||||||
- 'db/migrate/20160227230233_add_attachment_avatar_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160305115639_add_devise_to_users.rb'
|
|
||||||
- 'db/migrate/20160306172223_create_doorkeeper_tables.rb'
|
|
||||||
- 'db/migrate/20160312193225_add_attachment_header_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160314164231_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20160316103650_add_missing_indices.rb'
|
|
||||||
- 'db/migrate/20160322193748_add_avatar_remote_url_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160325130944_add_admin_to_users.rb'
|
|
||||||
- 'db/migrate/20160826155805_add_superapp_to_oauth_applications.rb'
|
|
||||||
- 'db/migrate/20160905150353_create_media_attachments.rb'
|
|
||||||
- 'db/migrate/20160919221059_add_subscription_expires_at_to_accounts.rb'
|
|
||||||
- 'db/migrate/20160920003904_remove_verify_token_from_accounts.rb'
|
|
||||||
- 'db/migrate/20160926213048_remove_owner_from_application.rb'
|
|
||||||
- 'db/migrate/20161003142332_add_confirmable_to_users.rb'
|
|
||||||
- 'db/migrate/20161003145426_create_blocks.rb'
|
|
||||||
- 'db/migrate/20161006213403_rails_settings_migration.rb'
|
|
||||||
- 'db/migrate/20161009120834_create_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20161027172456_add_silenced_to_accounts.rb'
|
|
||||||
- 'db/migrate/20161104173623_create_tags.rb'
|
|
||||||
- 'db/migrate/20161105130633_create_statuses_tags_join_table.rb'
|
|
||||||
- 'db/migrate/20161116162355_add_locale_to_users.rb'
|
|
||||||
- 'db/migrate/20161119211120_create_notifications.rb'
|
|
||||||
- 'db/migrate/20161122163057_remove_unneeded_indexes.rb'
|
|
||||||
- 'db/migrate/20161123093447_add_sensitive_to_statuses.rb'
|
|
||||||
- 'db/migrate/20161128103007_create_subscriptions.rb'
|
|
||||||
- 'db/migrate/20161130142058_add_last_successful_delivery_at_to_subscriptions.rb'
|
|
||||||
- 'db/migrate/20161130185319_add_visibility_to_statuses.rb'
|
|
||||||
- 'db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb'
|
|
||||||
- 'db/migrate/20161203164520_add_from_account_id_to_notifications.rb'
|
|
||||||
- 'db/migrate/20161205214545_add_suspended_to_accounts.rb'
|
|
||||||
- 'db/migrate/20161221152630_add_hidden_to_stream_entries.rb'
|
|
||||||
- 'db/migrate/20161222201034_add_locked_to_accounts.rb'
|
|
||||||
- 'db/migrate/20161222204147_create_follow_requests.rb'
|
|
||||||
- 'db/migrate/20170105224407_add_shortcode_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20170109120109_create_web_settings.rb'
|
|
||||||
- 'db/migrate/20170112154826_migrate_settings.rb'
|
|
||||||
- 'db/migrate/20170114194937_add_application_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170114203041_add_website_to_oauth_application.rb'
|
|
||||||
- 'db/migrate/20170119214911_create_preview_cards.rb'
|
|
||||||
- 'db/migrate/20170123162658_add_severity_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20170123203248_add_reject_media_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20170125145934_add_spoiler_text_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb'
|
|
||||||
- 'db/migrate/20170205175257_remove_devices.rb'
|
|
||||||
- 'db/migrate/20170209184350_add_reply_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170214110202_create_reports.rb'
|
|
||||||
- 'db/migrate/20170217012631_add_reblog_of_id_foreign_key_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170301222600_create_mutes.rb'
|
|
||||||
- 'db/migrate/20170303212857_add_last_emailed_at_to_users.rb'
|
|
||||||
- 'db/migrate/20170304202101_add_type_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20170317193015_add_search_index_to_accounts.rb'
|
|
||||||
- 'db/migrate/20170318214217_add_header_remote_url_to_accounts.rb'
|
|
||||||
- 'db/migrate/20170322021028_add_lowercase_index_to_accounts.rb'
|
|
||||||
- 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
|
|
||||||
- 'db/migrate/20170322162804_add_search_index_to_tags.rb'
|
|
||||||
- 'db/migrate/20170330021336_add_counter_caches.rb'
|
|
||||||
- 'db/migrate/20170330163835_create_imports.rb'
|
|
||||||
- 'db/migrate/20170330164118_add_attachment_data_to_imports.rb'
|
|
||||||
- 'db/migrate/20170403172249_add_action_taken_by_account_id_to_reports.rb'
|
|
||||||
- 'db/migrate/20170405112956_add_index_on_mentions_status_id.rb'
|
|
||||||
- 'db/migrate/20170406215816_add_notifications_and_favourites_indices.rb'
|
|
||||||
- 'db/migrate/20170409170753_add_last_webfingered_at_to_accounts.rb'
|
|
||||||
- 'db/migrate/20170414080609_add_devise_two_factor_backupable_to_users.rb'
|
|
||||||
- 'db/migrate/20170414132105_add_language_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170418160728_add_indexes_to_reports_for_accounts.rb'
|
|
||||||
- 'db/migrate/20170423005413_add_allowed_languages_to_user.rb'
|
|
||||||
- 'db/migrate/20170424003227_create_account_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20170424112722_add_status_id_index_to_statuses_tags.rb'
|
|
||||||
- 'db/migrate/20170425131920_add_media_attachment_meta.rb'
|
|
||||||
- 'db/migrate/20170425202925_add_oembed_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20170427011934_re_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20170506235850_create_conversations.rb'
|
|
||||||
- 'db/migrate/20170507000211_add_conversation_id_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170507141759_optimize_index_subscriptions.rb'
|
|
||||||
- 'db/migrate/20170508230434_create_conversation_mutes.rb'
|
|
||||||
- 'db/migrate/20170516072309_add_index_accounts_on_uri.rb'
|
|
||||||
- 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
|
|
||||||
- 'db/migrate/20170601210557_add_index_on_media_attachments_account_id.rb'
|
|
||||||
- 'db/migrate/20170604144747_add_foreign_keys_for_accounts.rb'
|
|
||||||
- 'db/migrate/20170606113804_change_tag_search_index_to_btree.rb'
|
|
||||||
- 'db/migrate/20170609145826_remove_default_language_from_statuses.rb'
|
|
||||||
- 'db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb'
|
|
||||||
- 'db/migrate/20170623152212_create_session_activations.rb'
|
|
||||||
- 'db/migrate/20170624134742_add_description_to_session_activations.rb'
|
|
||||||
- 'db/migrate/20170625140443_add_access_token_id_to_session_activations.rb'
|
|
||||||
- 'db/migrate/20170711225116_fix_null_booleans.rb'
|
|
||||||
- 'db/migrate/20170713112503_make_tag_search_case_insensitive.rb'
|
|
||||||
- 'db/migrate/20170713175513_create_web_push_subscriptions.rb'
|
|
||||||
- 'db/migrate/20170713190709_add_web_push_subscription_to_session_activations.rb'
|
|
||||||
- 'db/migrate/20170714184731_add_domain_to_subscriptions.rb'
|
|
||||||
- 'db/migrate/20170716191202_add_hide_notifications_to_mute.rb'
|
|
||||||
- 'db/migrate/20170718211102_add_activitypub_to_accounts.rb'
|
|
||||||
- 'db/migrate/20170720000000_add_index_favourites_on_account_id_and_id.rb'
|
|
||||||
- 'db/migrate/20170823162448_create_status_pins.rb'
|
|
||||||
- 'db/migrate/20170824103029_add_timestamps_to_status_pins.rb'
|
|
||||||
- 'db/migrate/20170829215220_remove_status_pins_account_index.rb'
|
|
||||||
- 'db/migrate/20170901141119_truncate_preview_cards.rb'
|
|
||||||
- 'db/migrate/20170901142658_create_join_table_preview_cards_statuses.rb'
|
|
||||||
- 'db/migrate/20170905044538_add_index_id_account_id_activity_type_on_notifications.rb'
|
|
||||||
- 'db/migrate/20170905165803_add_local_to_statuses.rb'
|
|
||||||
- 'db/migrate/20170913000752_create_site_uploads.rb'
|
|
||||||
- 'db/migrate/20170917153509_create_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20170918125918_ids_to_bigints.rb'
|
|
||||||
- 'db/migrate/20170920024819_status_ids_to_timestamp_ids.rb'
|
|
||||||
- 'db/migrate/20170920032311_fix_reblogs_in_feeds.rb'
|
|
||||||
- 'db/migrate/20170924022025_ids_to_bigints2.rb'
|
|
||||||
- 'db/migrate/20170927215609_add_description_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20170928082043_create_email_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20171005102658_create_account_moderation_notes.rb'
|
|
||||||
- 'db/migrate/20171005171936_add_disabled_to_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20171006142024_add_uri_to_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb'
|
|
||||||
- 'db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb'
|
|
||||||
- 'db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb'
|
|
||||||
- 'db/migrate/20171028221157_add_reblogs_to_follows.rb'
|
|
||||||
- 'db/migrate/20171107143332_add_memorial_to_accounts.rb'
|
|
||||||
- 'db/migrate/20171107143624_add_disabled_to_users.rb'
|
|
||||||
- 'db/migrate/20171109012327_add_moderator_to_accounts.rb'
|
|
||||||
- 'db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20171114231651_create_lists.rb'
|
|
||||||
- 'db/migrate/20171116161857_create_list_accounts.rb'
|
|
||||||
- 'db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb'
|
|
||||||
- 'db/migrate/20171119172437_create_admin_action_logs.rb'
|
|
||||||
- 'db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb'
|
|
||||||
- 'db/migrate/20171125024930_create_invites.rb'
|
|
||||||
- 'db/migrate/20171125031751_add_invite_id_to_users.rb'
|
|
||||||
- 'db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb'
|
|
||||||
- 'db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb'
|
|
||||||
- 'db/migrate/20171129172043_add_index_on_stream_entries.rb'
|
|
||||||
- 'db/migrate/20171130000000_add_embed_url_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb'
|
|
||||||
- 'db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb'
|
|
||||||
- 'db/migrate/20171226094803_more_faster_index_on_notifications.rb'
|
|
||||||
- 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
|
|
||||||
- 'db/migrate/20180109143959_add_remember_token_to_users.rb'
|
|
||||||
- 'db/migrate/20180204034416_create_identities.rb'
|
|
||||||
- 'db/migrate/20180206000000_change_user_id_nonnullable.rb'
|
|
||||||
- 'db/migrate/20180211015820_create_backups.rb'
|
|
||||||
- 'db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb'
|
|
||||||
- 'db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb'
|
|
||||||
- 'db/migrate/20180402031200_add_assigned_account_id_to_reports.rb'
|
|
||||||
- 'db/migrate/20180402040909_create_report_notes.rb'
|
|
||||||
- 'db/migrate/20180410204633_add_fields_to_accounts.rb'
|
|
||||||
- 'db/migrate/20180416210259_add_uri_to_relationships.rb'
|
|
||||||
- 'db/migrate/20180506221944_add_actor_type_to_accounts.rb'
|
|
||||||
- 'db/migrate/20180510214435_add_access_token_id_to_web_push_subscriptions.rb'
|
|
||||||
- 'db/migrate/20180510230049_migrate_web_push_subscriptions.rb'
|
|
||||||
- 'db/migrate/20180528141303_fix_accounts_unique_index.rb'
|
|
||||||
- 'db/migrate/20180608213548_reject_following_blocked_users.rb'
|
|
||||||
- 'db/migrate/20180609104432_migrate_web_push_subscriptions2.rb'
|
|
||||||
- 'db/migrate/20180615122121_add_autofollow_to_invites.rb'
|
|
||||||
- 'db/migrate/20180616192031_add_chosen_languages_to_users.rb'
|
|
||||||
- 'db/migrate/20180617162849_remove_unused_indexes.rb'
|
|
||||||
- 'db/migrate/20180628181026_create_custom_filters.rb'
|
|
||||||
- 'db/migrate/20180707154237_add_whole_word_to_custom_filter.rb'
|
|
||||||
- 'db/migrate/20180711152640_create_relays.rb'
|
|
||||||
- 'db/migrate/20180808175627_create_account_pins.rb'
|
|
||||||
- 'db/migrate/20180812123222_change_relays_enabled.rb'
|
|
||||||
- 'db/migrate/20180812162710_create_status_stats.rb'
|
|
||||||
- 'db/migrate/20180812173710_copy_status_stats.rb'
|
|
||||||
- 'db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb'
|
|
||||||
- 'db/migrate/20180831171112_create_bookmarks.rb'
|
|
||||||
- 'db/migrate/20180929222014_create_account_conversations.rb'
|
|
||||||
- 'db/migrate/20181007025445_create_pghero_space_stats.rb'
|
|
||||||
- 'db/migrate/20181010141500_add_silent_to_mentions.rb'
|
|
||||||
- 'db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20181018205649_add_unread_to_account_conversations.rb'
|
|
||||||
- 'db/migrate/20181024224956_migrate_account_conversations.rb'
|
|
||||||
- 'db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb'
|
|
||||||
- 'db/migrate/20181116165755_create_account_stats.rb'
|
|
||||||
- 'db/migrate/20181116173541_copy_account_stats.rb'
|
|
||||||
- 'db/migrate/20181127130500_identity_id_to_bigint.rb'
|
|
||||||
- 'db/migrate/20181127165847_add_show_replies_to_lists.rb'
|
|
||||||
- 'db/migrate/20181203003808_create_accounts_tags_join_table.rb'
|
|
||||||
- 'db/migrate/20181203021853_add_discoverable_to_accounts.rb'
|
|
||||||
- 'db/migrate/20181204193439_add_last_status_at_to_account_stats.rb'
|
|
||||||
- 'db/migrate/20181204215309_create_account_tag_stats.rb'
|
|
||||||
- 'db/migrate/20181207011115_downcase_custom_emoji_domains.rb'
|
|
||||||
- 'db/migrate/20181213184704_create_account_warnings.rb'
|
|
||||||
- 'db/migrate/20181213185533_create_account_warning_presets.rb'
|
|
||||||
- 'db/migrate/20181219235220_add_created_by_application_id_to_users.rb'
|
|
||||||
- 'db/migrate/20181226021420_add_also_known_as_to_accounts.rb'
|
|
||||||
- 'db/migrate/20190103124649_create_scheduled_statuses.rb'
|
|
||||||
- 'db/migrate/20190103124754_add_scheduled_status_id_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20190117114553_create_tombstones.rb'
|
|
||||||
- 'db/migrate/20190201012802_add_overwrite_to_imports.rb'
|
|
||||||
- 'db/migrate/20190203180359_create_featured_tags.rb'
|
|
||||||
- 'db/migrate/20190225031541_create_polls.rb'
|
|
||||||
- 'db/migrate/20190225031625_create_poll_votes.rb'
|
|
||||||
- 'db/migrate/20190226003449_add_poll_id_to_statuses.rb'
|
|
||||||
- 'db/migrate/20190304152020_add_uri_to_poll_votes.rb'
|
|
||||||
- 'db/migrate/20190306145741_add_lock_version_to_polls.rb'
|
|
||||||
- 'db/migrate/20190307234537_add_approved_to_users.rb'
|
|
||||||
- 'db/migrate/20190314181829_migrate_open_registrations_setting.rb'
|
|
||||||
- 'db/migrate/20190316190352_create_account_identity_proofs.rb'
|
|
||||||
- 'db/migrate/20190317135723_add_uri_to_reports.rb'
|
|
||||||
- 'db/migrate/20190403141604_add_comment_to_invites.rb'
|
|
||||||
- 'db/migrate/20190409054914_create_user_invite_requests.rb'
|
|
||||||
- 'db/migrate/20190420025523_add_blurhash_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20190509164208_add_by_moderator_to_tombstone.rb'
|
|
||||||
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
|
|
||||||
- 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
|
|
||||||
- 'db/migrate/20190627222225_create_custom_emoji_categories.rb'
|
|
||||||
- 'db/migrate/20190627222826_add_category_id_to_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20190701022101_add_trust_level_to_accounts.rb'
|
|
||||||
- 'db/migrate/20190705002136_create_domain_allows.rb'
|
|
||||||
- 'db/migrate/20190715164535_add_instance_actor.rb'
|
|
||||||
- 'db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb'
|
|
||||||
- 'db/migrate/20190729185330_add_score_to_tags.rb'
|
|
||||||
- 'db/migrate/20190805123746_add_capabilities_to_tags.rb'
|
|
||||||
- 'db/migrate/20190807135426_add_comments_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20190815225426_add_last_status_at_to_tags.rb'
|
|
||||||
- 'db/migrate/20190819134503_add_deleted_at_to_statuses.rb'
|
|
||||||
- 'db/migrate/20190820003045_update_statuses_index.rb'
|
|
||||||
- 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
|
|
||||||
- 'db/migrate/20190901035623_add_max_score_to_tags.rb'
|
|
||||||
- 'db/migrate/20190904222339_create_markers.rb'
|
|
||||||
- 'db/migrate/20190914202517_create_account_migrations.rb'
|
|
||||||
- 'db/migrate/20190915194355_create_account_aliases.rb'
|
|
||||||
- 'db/migrate/20190927232842_add_voters_count_to_polls.rb'
|
|
||||||
- 'db/migrate/20191001213028_add_lock_version_to_account_stats.rb'
|
|
||||||
- 'db/migrate/20191007013357_update_pt_locales.rb'
|
|
||||||
- 'db/migrate/20191031163205_change_list_account_follow_nullable.rb'
|
|
||||||
- 'db/migrate/20191212003415_increase_backup_size.rb'
|
|
||||||
- 'db/migrate/20191212163405_add_hide_collections_to_accounts.rb'
|
|
||||||
- 'db/migrate/20191218153258_create_announcements.rb'
|
|
||||||
- 'db/migrate/20200113125135_create_announcement_mutes.rb'
|
|
||||||
- 'db/migrate/20200114113335_create_announcement_reactions.rb'
|
|
||||||
- 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
|
|
||||||
- 'db/migrate/20200126203551_add_published_at_to_announcements.rb'
|
|
||||||
- 'db/migrate/20200306035625_add_processing_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20200309150742_add_forwarded_to_reports.rb'
|
|
||||||
- 'db/migrate/20200312144258_add_title_to_account_warning_presets.rb'
|
|
||||||
- 'db/migrate/20200312162302_add_status_ids_to_announcements.rb'
|
|
||||||
- 'db/migrate/20200312185443_add_parent_id_to_email_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20200317021758_add_expires_at_to_mutes.rb'
|
|
||||||
- 'db/migrate/20200407201300_create_unavailable_domains.rb'
|
|
||||||
- 'db/migrate/20200407202420_migrate_unavailable_inboxes.rb'
|
|
||||||
- 'db/migrate/20200417125749_add_storage_schema_version.rb'
|
|
||||||
- 'db/migrate/20200508212852_reset_unique_jobs_locks.rb'
|
|
||||||
- 'db/migrate/20200510110808_reset_web_app_secret.rb'
|
|
||||||
- 'db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb'
|
|
||||||
- 'db/migrate/20200516180352_create_devices.rb'
|
|
||||||
- 'db/migrate/20200516183822_create_one_time_keys.rb'
|
|
||||||
- 'db/migrate/20200518083523_create_encrypted_messages.rb'
|
|
||||||
- 'db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb'
|
|
||||||
- 'db/migrate/20200529214050_add_devices_url_to_accounts.rb'
|
|
||||||
- 'db/migrate/20200601222558_create_system_keys.rb'
|
|
||||||
- 'db/migrate/20200605155027_add_blurhash_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20200608113046_add_sign_in_token_to_users.rb'
|
|
||||||
- 'db/migrate/20200614002136_add_sensitized_to_accounts.rb'
|
|
||||||
- 'db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb'
|
|
||||||
- 'db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb'
|
|
||||||
- 'db/migrate/20200627125810_add_thumbnail_columns_to_media_attachments.rb'
|
|
||||||
- 'db/migrate/20200628133322_create_account_notes.rb'
|
|
||||||
- 'db/migrate/20200630190240_create_webauthn_credentials.rb'
|
|
||||||
- 'db/migrate/20200630190544_add_webauthn_id_to_users.rb'
|
|
||||||
- 'db/migrate/20200908193330_create_account_deletion_requests.rb'
|
|
||||||
- 'db/migrate/20200917192924_add_notify_to_follows.rb'
|
|
||||||
- 'db/migrate/20200917193034_add_type_to_notifications.rb'
|
|
||||||
- 'db/migrate/20200917222316_add_index_notifications_on_type.rb'
|
|
||||||
- 'db/migrate/20201008202037_create_ip_blocks.rb'
|
|
||||||
- 'db/migrate/20201008220312_add_sign_up_ip_to_users.rb'
|
|
||||||
- 'db/migrate/20201017233919_add_suspension_origin_to_accounts.rb'
|
|
||||||
- 'db/migrate/20201206004238_create_instances.rb'
|
|
||||||
- 'db/migrate/20201218054746_add_obfuscate_to_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20210221045109_create_rules.rb'
|
|
||||||
- 'db/migrate/20210306164523_account_ids_to_timestamp_ids.rb'
|
|
||||||
- 'db/migrate/20210322164601_create_account_summaries.rb'
|
|
||||||
- 'db/migrate/20210323114347_create_follow_recommendations.rb'
|
|
||||||
- 'db/migrate/20210324171613_create_follow_recommendation_suppressions.rb'
|
|
||||||
- 'db/migrate/20210416200740_create_canonical_email_blocks.rb'
|
|
||||||
- 'db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb'
|
|
||||||
- 'db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb'
|
|
||||||
- 'db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb'
|
|
||||||
- 'db/migrate/20210609202149_create_login_activities.rb'
|
|
||||||
- 'db/migrate/20210616214526_create_user_ips.rb'
|
|
||||||
- 'db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb'
|
|
||||||
- 'db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb'
|
|
||||||
- 'db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb'
|
|
||||||
- 'db/migrate/20210904215403_add_edited_at_to_statuses.rb'
|
|
||||||
- 'db/migrate/20210908220918_create_status_edits.rb'
|
|
||||||
- 'db/migrate/20211031031021_create_preview_card_providers.rb'
|
|
||||||
- 'db/migrate/20211112011713_add_language_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20211115032527_add_trendable_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20211123212714_add_link_type_to_preview_cards.rb'
|
|
||||||
- 'db/migrate/20211213040746_update_account_summaries_to_version_2.rb'
|
|
||||||
- 'db/migrate/20211231080958_add_category_to_reports.rb'
|
|
||||||
- 'db/migrate/20220105163928_remove_mentions_status_id_index.rb'
|
|
||||||
- 'db/migrate/20220115125126_add_report_id_to_account_warnings.rb'
|
|
||||||
- 'db/migrate/20220115125341_fix_account_warning_actions.rb'
|
|
||||||
- 'db/migrate/20220116202951_add_deleted_at_index_on_statuses.rb'
|
|
||||||
- 'db/migrate/20220124141035_create_appeals.rb'
|
|
||||||
- 'db/migrate/20220202200743_add_trendable_to_accounts.rb'
|
|
||||||
- 'db/migrate/20220202200926_add_trendable_to_statuses.rb'
|
|
||||||
- 'db/migrate/20220210153119_add_overruled_at_to_account_warnings.rb'
|
|
||||||
- 'db/migrate/20220224010024_add_ips_to_email_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20220227041951_add_last_used_at_to_oauth_access_tokens.rb'
|
|
||||||
- 'db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb'
|
|
||||||
- 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb'
|
|
||||||
- 'db/migrate/20220304195405_migrate_hide_network_preference.rb'
|
|
||||||
- 'db/migrate/20220307094650_fix_featured_tags_constraints.rb'
|
|
||||||
- 'db/migrate/20220309213005_fix_reblog_deleted_at.rb'
|
|
||||||
- 'db/migrate/20220316233212_update_kurdish_locales.rb'
|
|
||||||
- 'db/migrate/20220428112511_add_index_statuses_on_account_id.rb'
|
|
||||||
- 'db/migrate/20220428112727_add_index_statuses_pins_on_status_id.rb'
|
|
||||||
- 'db/migrate/20220428114454_add_index_reports_on_assigned_account_id.rb'
|
|
||||||
- 'db/migrate/20220428114902_add_index_reports_on_action_taken_by_account_id.rb'
|
|
||||||
- 'db/migrate/20220606044941_create_webhooks.rb'
|
|
||||||
- 'db/migrate/20220611210335_create_user_roles.rb'
|
|
||||||
- 'db/migrate/20220611212541_add_role_id_to_users.rb'
|
|
||||||
- 'db/migrate/20220710102457_add_display_name_to_tags.rb'
|
|
||||||
- 'db/migrate/20220714171049_create_tag_follows.rb'
|
|
||||||
- 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb'
|
|
||||||
- 'db/migrate/20220824233535_create_status_trends.rb'
|
|
||||||
- 'db/migrate/20220827195229_change_canonical_email_blocks_nullable.rb'
|
|
||||||
- 'db/migrate/20220829192633_add_languages_to_follows.rb'
|
|
||||||
- 'db/migrate/20220829192658_add_languages_to_follow_requests.rb'
|
|
||||||
- 'db/migrate/20221006061337_create_preview_card_trends.rb'
|
|
||||||
- 'db/migrate/20221012181003_add_blurhash_to_site_uploads.rb'
|
|
||||||
- 'db/migrate/20221021055441_add_index_featured_tags_on_account_id_and_tag_id.rb'
|
|
||||||
- 'db/migrate/20221025171544_add_index_ip_blocks_on_ip.rb'
|
|
||||||
- 'db/migrate/20221104133904_add_name_to_featured_tags.rb'
|
|
||||||
- 'db/post_migrate/20190519130537_remove_boosts_widening_audience.rb'
|
|
||||||
- 'db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb'
|
|
||||||
- 'db/post_migrate/20220118183123_remove_rememberable_from_users.rb'
|
|
||||||
- 'db/seeds/01_web_app.rb'
|
|
||||||
- 'db/seeds/02_instance_actor.rb'
|
|
||||||
- 'db/seeds/03_roles.rb'
|
|
||||||
- 'db/seeds/04_admin.rb'
|
|
||||||
- 'lib/rails/engine_extensions.rb'
|
|
||||||
- 'lib/tasks/branding.rake'
|
|
||||||
- 'spec/fabricators_spec.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
Style/GlobalStdStream:
|
Style/GlobalStdStream:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -1277,7 +713,6 @@ Style/MutableConstant:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/models/tag.rb'
|
- 'app/models/tag.rb'
|
||||||
- 'app/services/delete_account_service.rb'
|
- 'app/services/delete_account_service.rb'
|
||||||
- 'config/initializers/twitter_regex.rb'
|
|
||||||
- 'lib/mastodon/migration_warning.rb'
|
- 'lib/mastodon/migration_warning.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
|
@ -1319,8 +754,6 @@ Style/RedundantConstantBase:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/environments/production.rb'
|
- 'config/environments/production.rb'
|
||||||
- 'config/initializers/sidekiq.rb'
|
- 'config/initializers/sidekiq.rb'
|
||||||
- 'config/locales/sr-Latn.rb'
|
|
||||||
- 'config/locales/sr.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
# Configuration parameters: SafeForConstants.
|
# Configuration parameters: SafeForConstants.
|
||||||
|
@ -1340,13 +773,6 @@ Style/SafeNavigation:
|
||||||
- 'app/models/concerns/account_finder_concern.rb'
|
- 'app/models/concerns/account_finder_concern.rb'
|
||||||
- 'app/models/status.rb'
|
- 'app/models/status.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: AllowAsExpressionSeparator.
|
|
||||||
Style/Semicolon:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/services/activitypub/process_status_update_service_spec.rb'
|
|
||||||
- 'spec/validators/blacklisted_email_validator_spec.rb'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: only_raise, only_fail, semantic
|
# SupportedStyles: only_raise, only_fail, semantic
|
||||||
|
@ -1360,21 +786,6 @@ Style/SingleArgumentDig:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/webpacker/manifest_extensions.rb'
|
- 'lib/webpacker/manifest_extensions.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
Style/SlicingWithRange:
|
|
||||||
Exclude:
|
|
||||||
- 'app/lib/emoji_formatter.rb'
|
|
||||||
- 'app/lib/text_formatter.rb'
|
|
||||||
- 'app/models/account_alias.rb'
|
|
||||||
- 'app/models/domain_block.rb'
|
|
||||||
- 'app/models/email_domain_block.rb'
|
|
||||||
- 'app/models/preview_card_provider.rb'
|
|
||||||
- 'app/validators/status_length_validator.rb'
|
|
||||||
- 'db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb'
|
|
||||||
- 'lib/active_record/batches.rb'
|
|
||||||
- 'lib/mastodon/premailer_webpack_strategy.rb'
|
|
||||||
- 'lib/tasks/repo.rake'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: require_parentheses, require_no_parentheses
|
# SupportedStyles: require_parentheses, require_no_parentheses
|
||||||
|
@ -1444,9 +855,3 @@ Style/WordArray:
|
||||||
- 'config/initializers/cors.rb'
|
- 'config/initializers/cors.rb'
|
||||||
- 'spec/controllers/settings/imports_controller_spec.rb'
|
- 'spec/controllers/settings/imports_controller_spec.rb'
|
||||||
- 'spec/models/form/import_spec.rb'
|
- 'spec/models/form/import_spec.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
|
||||||
# URISchemes: http, https
|
|
||||||
Layout/LineLength:
|
|
||||||
Max: 701
|
|
||||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -2,6 +2,34 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.1.6] - 2023-07-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix memory leak in streaming server ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26228))
|
||||||
|
- Fix wrong filters sometimes applying in streaming ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26159), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26213), [renchap](https://github.com/mastodon/mastodon/pull/26233))
|
||||||
|
- Fix incorrect connect timeout in outgoing requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26116))
|
||||||
|
|
||||||
|
## [4.1.5] - 2023-07-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add check preventing Sidekiq workers from running with Makara configured ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25850))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change request timeout handling to use a longer deadline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26055))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix moderation interface for remote instances with a .zip TLD ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25885))
|
||||||
|
- Fix remote accounts being possibly persisted to database with incomplete protocol values ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886))
|
||||||
|
- Fix trending publishers table not rendering correctly on narrow screens ([vmstan](https://github.com/mastodon/mastodon/pull/25945))
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Fix CSP headers being unintentionally wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26105))
|
||||||
|
|
||||||
## [4.1.4] - 2023-07-07
|
## [4.1.4] - 2023-07-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -143,7 +171,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Add instance activity API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22833))
|
- Add instance activity API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22833))
|
||||||
- Add setting for status page URL ([Gargron](https://github.com/mastodon/mastodon/pull/23390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23499))
|
- Add setting for status page URL ([Gargron](https://github.com/mastodon/mastodon/pull/23390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23499))
|
||||||
- REST API changes:
|
- REST API changes:
|
||||||
- Add `configuration.urls.status` attribute to the object returned by `GET /api/v1/instance`
|
- Add `configuration.urls.status` attribute to the object returned by `GET /api/v2/instance`
|
||||||
- Add `account.approved` webhook ([Saiv46](https://github.com/mastodon/mastodon/pull/22938))
|
- Add `account.approved` webhook ([Saiv46](https://github.com/mastodon/mastodon/pull/22938))
|
||||||
- Add 12 hours option to polls ([Pleclown](https://github.com/mastodon/mastodon/pull/21131))
|
- Add 12 hours option to polls ([Pleclown](https://github.com/mastodon/mastodon/pull/21131))
|
||||||
- Add dropdown menu item to open admin interface for remote domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21895))
|
- Add dropdown menu item to open admin interface for remote domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21895))
|
||||||
|
|
22
Gemfile
22
Gemfile
|
@ -4,14 +4,13 @@ source 'https://rubygems.org'
|
||||||
ruby '>= 3.0.0'
|
ruby '>= 3.0.0'
|
||||||
|
|
||||||
gem 'puma', '~> 6.3'
|
gem 'puma', '~> 6.3'
|
||||||
gem 'rails', '~> 6.1.7'
|
gem 'rails', '~> 7.0'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'sprockets', '~> 3.7.2'
|
||||||
gem 'thor', '~> 1.2'
|
gem 'thor', '~> 1.2'
|
||||||
gem 'rack', '~> 2.2.7'
|
gem 'rack', '~> 2.2.7'
|
||||||
|
|
||||||
gem 'haml-rails', '~>2.0'
|
gem 'haml-rails', '~>2.0'
|
||||||
gem 'pg', '~> 1.5'
|
gem 'pg', '~> 1.5'
|
||||||
gem 'makara', '~> 0.5'
|
|
||||||
gem 'pghero'
|
gem 'pghero'
|
||||||
gem 'dotenv-rails', '~> 2.8'
|
gem 'dotenv-rails', '~> 2.8'
|
||||||
|
|
||||||
|
@ -19,6 +18,7 @@ gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||||
gem 'fog-core', '<= 2.4.0'
|
gem 'fog-core', '<= 2.4.0'
|
||||||
gem 'fog-openstack', '~> 0.3', require: false
|
gem 'fog-openstack', '~> 0.3', require: false
|
||||||
gem 'kt-paperclip', '~> 7.2'
|
gem 'kt-paperclip', '~> 7.2'
|
||||||
|
gem 'md-paperclip-azure', '~> 2.2', require: false
|
||||||
gem 'blurhash', '~> 0.1'
|
gem 'blurhash', '~> 0.1'
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
|
@ -67,7 +67,7 @@ gem 'pundit', '~> 2.3'
|
||||||
gem 'premailer-rails'
|
gem 'premailer-rails'
|
||||||
gem 'rack-attack', '~> 6.6'
|
gem 'rack-attack', '~> 6.6'
|
||||||
gem 'rack-cors', '~> 2.0', require: 'rack/cors'
|
gem 'rack-cors', '~> 2.0', require: 'rack/cors'
|
||||||
gem 'rails-i18n', '~> 6.0'
|
gem 'rails-i18n', '~> 7.0'
|
||||||
gem 'rails-settings-cached', '~> 0.6', git: 'https://github.com/mastodon/rails-settings-cached.git', branch: 'v0.6.6-aliases-true'
|
gem 'rails-settings-cached', '~> 0.6', git: 'https://github.com/mastodon/rails-settings-cached.git', branch: 'v0.6.6-aliases-true'
|
||||||
gem 'redcarpet', '~> 3.6'
|
gem 'redcarpet', '~> 3.6'
|
||||||
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
|
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
|
||||||
|
@ -99,9 +99,6 @@ gem 'rdf-normalize', '~> 0.5'
|
||||||
gem 'private_address_check', '~> 0.5'
|
gem 'private_address_check', '~> 0.5'
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
# RSpec runner for rails
|
|
||||||
gem 'rspec-rails', '~> 6.0'
|
|
||||||
|
|
||||||
# Used to split testing into chunks in CI
|
# Used to split testing into chunks in CI
|
||||||
gem 'rspec_chunked', '~> 0.6'
|
gem 'rspec_chunked', '~> 0.6'
|
||||||
|
|
||||||
|
@ -113,6 +110,10 @@ group :test do
|
||||||
|
|
||||||
# Browser integration testing
|
# Browser integration testing
|
||||||
gem 'capybara', '~> 3.39'
|
gem 'capybara', '~> 3.39'
|
||||||
|
gem 'selenium-webdriver'
|
||||||
|
|
||||||
|
# Used to reset the database between system tests
|
||||||
|
gem 'database_cleaner-active_record'
|
||||||
|
|
||||||
# Used to mock environment variables
|
# Used to mock environment variables
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
|
@ -159,7 +160,7 @@ group :development do
|
||||||
gem 'letter_opener_web', '~> 2.0'
|
gem 'letter_opener_web', '~> 2.0'
|
||||||
|
|
||||||
# Security analysis CLI tools
|
# Security analysis CLI tools
|
||||||
gem 'brakeman', '~> 5.4', require: false
|
gem 'brakeman', '~> 6.0', require: false
|
||||||
gem 'bundler-audit', '~> 0.9', require: false
|
gem 'bundler-audit', '~> 0.9', require: false
|
||||||
|
|
||||||
# Linter CLI for HAML files
|
# Linter CLI for HAML files
|
||||||
|
@ -173,10 +174,17 @@ group :development do
|
||||||
|
|
||||||
# Validate missing i18n keys
|
# Validate missing i18n keys
|
||||||
gem 'i18n-tasks', '~> 1.0', require: false
|
gem 'i18n-tasks', '~> 1.0', require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
# Profiling tools
|
# Profiling tools
|
||||||
gem 'memory_profiler', require: false
|
gem 'memory_profiler', require: false
|
||||||
|
gem 'ruby-prof', require: false
|
||||||
gem 'stackprof', require: false
|
gem 'stackprof', require: false
|
||||||
|
gem 'test-prof'
|
||||||
|
|
||||||
|
# RSpec runner for rails
|
||||||
|
gem 'rspec-rails', '~> 6.0'
|
||||||
|
|
||||||
# foreman
|
# foreman
|
||||||
gem 'foreman'
|
gem 'foreman'
|
||||||
|
|
203
Gemfile.lock
203
Gemfile.lock
|
@ -18,40 +18,47 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (6.1.7.4)
|
actioncable (7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.1.7.4)
|
actionmailbox (7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
activejob (= 6.1.7.4)
|
activejob (= 7.0.6)
|
||||||
activerecord (= 6.1.7.4)
|
activerecord (= 7.0.6)
|
||||||
activestorage (= 6.1.7.4)
|
activestorage (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.1.7.4)
|
net-imap
|
||||||
actionpack (= 6.1.7.4)
|
net-pop
|
||||||
actionview (= 6.1.7.4)
|
net-smtp
|
||||||
activejob (= 6.1.7.4)
|
actionmailer (7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
|
actionview (= 7.0.6)
|
||||||
|
activejob (= 7.0.6)
|
||||||
|
activesupport (= 7.0.6)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.1.7.4)
|
actionpack (7.0.6)
|
||||||
actionview (= 6.1.7.4)
|
actionview (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
rack (~> 2.0, >= 2.0.9)
|
rack (~> 2.0, >= 2.2.4)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.1.7.4)
|
actiontext (7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
activerecord (= 6.1.7.4)
|
activerecord (= 7.0.6)
|
||||||
activestorage (= 6.1.7.4)
|
activestorage (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.1.7.4)
|
actionview (7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
|
@ -61,27 +68,26 @@ GEM
|
||||||
activemodel (>= 4.1, < 7.1)
|
activemodel (>= 4.1, < 7.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 (6.1.7.4)
|
activejob (7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.1.7.4)
|
activemodel (7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
activerecord (6.1.7.4)
|
activerecord (7.0.6)
|
||||||
activemodel (= 6.1.7.4)
|
activemodel (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
activestorage (6.1.7.4)
|
activestorage (7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
activejob (= 6.1.7.4)
|
activejob (= 7.0.6)
|
||||||
activerecord (= 6.1.7.4)
|
activerecord (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
mini_mime (>= 1.1.0)
|
||||||
activesupport (6.1.7.4)
|
activesupport (7.0.6)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
zeitwerk (~> 2.3)
|
|
||||||
addressable (2.8.4)
|
addressable (2.8.4)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
aes_key_wrap (1.1.0)
|
aes_key_wrap (1.1.0)
|
||||||
|
@ -97,21 +103,29 @@ GEM
|
||||||
attr_required (1.0.1)
|
attr_required (1.0.1)
|
||||||
awrence (1.2.1)
|
awrence (1.2.1)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.780.0)
|
aws-partitions (1.791.0)
|
||||||
aws-sdk-core (3.175.0)
|
aws-sdk-core (3.178.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.67.0)
|
aws-sdk-kms (1.71.0)
|
||||||
aws-sdk-core (~> 3, >= 3.174.0)
|
aws-sdk-core (~> 3, >= 3.177.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.126.0)
|
aws-sdk-s3 (1.131.0)
|
||||||
aws-sdk-core (~> 3, >= 3.174.0)
|
aws-sdk-core (~> 3, >= 3.177.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.6)
|
||||||
aws-sigv4 (1.5.2)
|
aws-sigv4 (1.6.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
azure-storage-blob (2.0.3)
|
||||||
|
azure-storage-common (~> 2.0)
|
||||||
|
nokogiri (~> 1, >= 1.10.8)
|
||||||
|
azure-storage-common (2.0.4)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
faraday_middleware (~> 1.0, >= 1.0.0.rc1)
|
||||||
|
net-http-persistent (~> 4.0)
|
||||||
|
nokogiri (~> 1, >= 1.10.8)
|
||||||
bcrypt (3.1.18)
|
bcrypt (3.1.18)
|
||||||
better_errors (2.10.1)
|
better_errors (2.10.1)
|
||||||
erubi (>= 1.0.0)
|
erubi (>= 1.0.0)
|
||||||
|
@ -130,7 +144,7 @@ GEM
|
||||||
blurhash (0.1.7)
|
blurhash (0.1.7)
|
||||||
bootsnap (1.16.0)
|
bootsnap (1.16.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (5.4.1)
|
brakeman (6.0.1)
|
||||||
browser (5.3.1)
|
browser (5.3.1)
|
||||||
brpoplpush-redis_script (0.1.3)
|
brpoplpush-redis_script (0.1.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||||
|
@ -146,7 +160,7 @@ GEM
|
||||||
sshkit (>= 1.9.0)
|
sshkit (>= 1.9.0)
|
||||||
capistrano-bundler (2.1.0)
|
capistrano-bundler (2.1.0)
|
||||||
capistrano (~> 3.1)
|
capistrano (~> 3.1)
|
||||||
capistrano-rails (1.6.2)
|
capistrano-rails (1.6.3)
|
||||||
capistrano (~> 3.1)
|
capistrano (~> 3.1)
|
||||||
capistrano-bundler (>= 1.1, < 3)
|
capistrano-bundler (>= 1.1, < 3)
|
||||||
capistrano-rbenv (2.2.0)
|
capistrano-rbenv (2.2.0)
|
||||||
|
@ -167,7 +181,7 @@ GEM
|
||||||
activesupport
|
activesupport
|
||||||
cbor (0.5.9.6)
|
cbor (0.5.9.6)
|
||||||
charlock_holmes (0.7.7)
|
charlock_holmes (0.7.7)
|
||||||
chewy (7.3.2)
|
chewy (7.3.3)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||||
elasticsearch-dsl
|
elasticsearch-dsl
|
||||||
|
@ -185,6 +199,10 @@ GEM
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.14.0)
|
css_parser (1.14.0)
|
||||||
addressable
|
addressable
|
||||||
|
database_cleaner-active_record (2.1.0)
|
||||||
|
activerecord (>= 5.a)
|
||||||
|
database_cleaner-core (~> 2.0.0)
|
||||||
|
database_cleaner-core (2.0.1)
|
||||||
date (3.3.3)
|
date (3.3.3)
|
||||||
debug_inspector (1.1.0)
|
debug_inspector (1.1.0)
|
||||||
devise (4.9.2)
|
devise (4.9.2)
|
||||||
|
@ -255,6 +273,8 @@ GEM
|
||||||
faraday-patron (1.0.0)
|
faraday-patron (1.0.0)
|
||||||
faraday-rack (1.0.0)
|
faraday-rack (1.0.0)
|
||||||
faraday-retry (1.0.3)
|
faraday-retry (1.0.3)
|
||||||
|
faraday_middleware (1.2.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
fast_blank (1.0.1)
|
fast_blank (1.0.1)
|
||||||
fastimage (2.2.7)
|
fastimage (2.2.7)
|
||||||
ffi (1.15.5)
|
ffi (1.15.5)
|
||||||
|
@ -292,7 +312,7 @@ GEM
|
||||||
activesupport (>= 5.1)
|
activesupport (>= 5.1)
|
||||||
haml (>= 4.0.6)
|
haml (>= 4.0.6)
|
||||||
railties (>= 5.1)
|
railties (>= 5.1)
|
||||||
haml_lint (0.46.0)
|
haml_lint (0.49.2)
|
||||||
haml (>= 4.0, < 6.2)
|
haml (>= 4.0, < 6.2)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
rainbow
|
rainbow
|
||||||
|
@ -401,12 +421,14 @@ GEM
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
makara (0.5.1)
|
|
||||||
activerecord (>= 5.2.0)
|
|
||||||
marcel (1.0.2)
|
marcel (1.0.2)
|
||||||
mario-redis-lock (1.2.1)
|
mario-redis-lock (1.2.1)
|
||||||
redis (>= 3.0.5)
|
redis (>= 3.0.5)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
|
md-paperclip-azure (2.2.0)
|
||||||
|
addressable (~> 2.5)
|
||||||
|
azure-storage-blob (~> 2.0.1)
|
||||||
|
hashie (~> 5.0)
|
||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mime-types (3.4.1)
|
mime-types (3.4.1)
|
||||||
|
@ -420,6 +442,8 @@ GEM
|
||||||
multipart-post (2.3.0)
|
multipart-post (2.3.0)
|
||||||
net-http (0.3.2)
|
net-http (0.3.2)
|
||||||
uri
|
uri
|
||||||
|
net-http-persistent (4.0.2)
|
||||||
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.3.6)
|
net-imap (0.3.6)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
|
@ -469,7 +493,7 @@ GEM
|
||||||
openssl-signature_algorithm (1.3.0)
|
openssl-signature_algorithm (1.3.0)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ox (2.14.16)
|
ox (2.14.17)
|
||||||
parallel (1.23.0)
|
parallel (1.23.0)
|
||||||
parser (3.2.2.3)
|
parser (3.2.2.3)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
|
@ -490,7 +514,7 @@ GEM
|
||||||
net-smtp
|
net-smtp
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
private_address_check (0.5.0)
|
private_address_check (0.5.0)
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.3)
|
||||||
puma (6.3.0)
|
puma (6.3.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.3.0)
|
pundit (2.3.0)
|
||||||
|
@ -512,21 +536,20 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails (6.1.7.4)
|
rails (7.0.6)
|
||||||
actioncable (= 6.1.7.4)
|
actioncable (= 7.0.6)
|
||||||
actionmailbox (= 6.1.7.4)
|
actionmailbox (= 7.0.6)
|
||||||
actionmailer (= 6.1.7.4)
|
actionmailer (= 7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
actiontext (= 6.1.7.4)
|
actiontext (= 7.0.6)
|
||||||
actionview (= 6.1.7.4)
|
actionview (= 7.0.6)
|
||||||
activejob (= 6.1.7.4)
|
activejob (= 7.0.6)
|
||||||
activemodel (= 6.1.7.4)
|
activemodel (= 7.0.6)
|
||||||
activerecord (= 6.1.7.4)
|
activerecord (= 7.0.6)
|
||||||
activestorage (= 6.1.7.4)
|
activestorage (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 6.1.7.4)
|
railties (= 7.0.6)
|
||||||
sprockets-rails (>= 2.0.0)
|
|
||||||
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)
|
||||||
|
@ -537,20 +560,21 @@ GEM
|
||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.6.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (~> 1.14)
|
nokogiri (~> 1.14)
|
||||||
rails-i18n (6.0.0)
|
rails-i18n (7.0.7)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 7)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (6.1.7.4)
|
railties (7.0.6)
|
||||||
actionpack (= 6.1.7.4)
|
actionpack (= 7.0.6)
|
||||||
activesupport (= 6.1.7.4)
|
activesupport (= 7.0.6)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
|
zeitwerk (~> 2.5)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rdf (3.2.11)
|
rdf (3.2.11)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.6.0)
|
rdf-normalize (0.6.1)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
redcarpet (3.6.0)
|
redcarpet (3.6.0)
|
||||||
redis (4.8.1)
|
redis (4.8.1)
|
||||||
|
@ -564,7 +588,7 @@ GEM
|
||||||
responders (3.1.0)
|
responders (3.1.0)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
rexml (3.2.5)
|
rexml (3.2.6)
|
||||||
rotp (6.2.2)
|
rotp (6.2.2)
|
||||||
rouge (4.1.2)
|
rouge (4.1.2)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
|
@ -593,7 +617,7 @@ GEM
|
||||||
sidekiq (>= 2.4.0)
|
sidekiq (>= 2.4.0)
|
||||||
rspec-support (3.12.0)
|
rspec-support (3.12.0)
|
||||||
rspec_chunked (0.6)
|
rspec_chunked (0.6)
|
||||||
rubocop (1.54.1)
|
rubocop (1.54.2)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
|
@ -613,7 +637,7 @@ GEM
|
||||||
rubocop-performance (1.18.0)
|
rubocop-performance (1.18.0)
|
||||||
rubocop (>= 1.7.0, < 2.0)
|
rubocop (>= 1.7.0, < 2.0)
|
||||||
rubocop-ast (>= 0.4.0)
|
rubocop-ast (>= 0.4.0)
|
||||||
rubocop-rails (2.19.1)
|
rubocop-rails (2.20.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.33.0, < 2.0)
|
rubocop (>= 1.33.0, < 2.0)
|
||||||
|
@ -621,6 +645,7 @@ GEM
|
||||||
rubocop (~> 1.33)
|
rubocop (~> 1.33)
|
||||||
rubocop-capybara (~> 2.17)
|
rubocop-capybara (~> 2.17)
|
||||||
rubocop-factory_bot (~> 2.22)
|
rubocop-factory_bot (~> 2.22)
|
||||||
|
ruby-prof (1.6.3)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-saml (1.15.0)
|
ruby-saml (1.15.0)
|
||||||
nokogiri (>= 1.13.10)
|
nokogiri (>= 1.13.10)
|
||||||
|
@ -637,6 +662,10 @@ GEM
|
||||||
scenic (1.7.0)
|
scenic (1.7.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
|
selenium-webdriver (4.9.1)
|
||||||
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
|
websocket (~> 1.0)
|
||||||
semantic_range (3.0.0)
|
semantic_range (3.0.0)
|
||||||
sidekiq (6.5.9)
|
sidekiq (6.5.9)
|
||||||
connection_pool (>= 2.2.5, < 3)
|
connection_pool (>= 2.2.5, < 3)
|
||||||
|
@ -673,7 +702,7 @@ GEM
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sshkit (1.21.4)
|
sshkit (1.21.5)
|
||||||
net-scp (>= 1.1.2)
|
net-scp (>= 1.1.2)
|
||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
stackprof (0.2.25)
|
stackprof (0.2.25)
|
||||||
|
@ -691,9 +720,10 @@ GEM
|
||||||
unicode-display_width (>= 1.1.1, < 3)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
terrapin (0.6.0)
|
terrapin (0.6.0)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
|
test-prof (1.2.1)
|
||||||
thor (1.2.2)
|
thor (1.2.2)
|
||||||
tilt (2.2.0)
|
tilt (2.2.0)
|
||||||
timeout (0.3.2)
|
timeout (0.4.0)
|
||||||
tpm-key_attestation (0.12.0)
|
tpm-key_attestation (0.12.0)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0)
|
||||||
|
@ -749,6 +779,7 @@ GEM
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
semantic_range (>= 2.3.0)
|
semantic_range (>= 2.3.0)
|
||||||
|
websocket (1.2.9)
|
||||||
websocket-driver (0.7.5)
|
websocket-driver (0.7.5)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
|
@ -770,7 +801,7 @@ DEPENDENCIES
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
blurhash (~> 0.1)
|
blurhash (~> 0.1)
|
||||||
bootsnap (~> 1.16.0)
|
bootsnap (~> 1.16.0)
|
||||||
brakeman (~> 5.4)
|
brakeman (~> 6.0)
|
||||||
browser
|
browser
|
||||||
bundler-audit (~> 0.9)
|
bundler-audit (~> 0.9)
|
||||||
capistrano (~> 3.17)
|
capistrano (~> 3.17)
|
||||||
|
@ -785,6 +816,7 @@ DEPENDENCIES
|
||||||
color_diff (~> 0.1)
|
color_diff (~> 0.1)
|
||||||
concurrent-ruby
|
concurrent-ruby
|
||||||
connection_pool
|
connection_pool
|
||||||
|
database_cleaner-active_record
|
||||||
devise (~> 4.9)
|
devise (~> 4.9)
|
||||||
devise-two-factor (~> 4.1)
|
devise-two-factor (~> 4.1)
|
||||||
devise_pam_authenticatable2 (~> 9.2)
|
devise_pam_authenticatable2 (~> 9.2)
|
||||||
|
@ -819,8 +851,8 @@ DEPENDENCIES
|
||||||
letter_opener_web (~> 2.0)
|
letter_opener_web (~> 2.0)
|
||||||
link_header (~> 0.0)
|
link_header (~> 0.0)
|
||||||
lograge (~> 0.12)
|
lograge (~> 0.12)
|
||||||
makara (~> 0.5)
|
|
||||||
mario-redis-lock (~> 1.2)
|
mario-redis-lock (~> 1.2)
|
||||||
|
md-paperclip-azure (~> 2.2)
|
||||||
memory_profiler
|
memory_profiler
|
||||||
mime-types (~> 3.4.1)
|
mime-types (~> 3.4.1)
|
||||||
net-http (~> 0.3.2)
|
net-http (~> 0.3.2)
|
||||||
|
@ -846,9 +878,9 @@ DEPENDENCIES
|
||||||
rack-attack (~> 6.6)
|
rack-attack (~> 6.6)
|
||||||
rack-cors (~> 2.0)
|
rack-cors (~> 2.0)
|
||||||
rack-test (~> 2.1)
|
rack-test (~> 2.1)
|
||||||
rails (~> 6.1.7)
|
rails (~> 7.0)
|
||||||
rails-controller-testing (~> 1.0)
|
rails-controller-testing (~> 1.0)
|
||||||
rails-i18n (~> 6.0)
|
rails-i18n (~> 7.0)
|
||||||
rails-settings-cached (~> 0.6)!
|
rails-settings-cached (~> 0.6)!
|
||||||
rdf-normalize (~> 0.5)
|
rdf-normalize (~> 0.5)
|
||||||
redcarpet (~> 3.6)
|
redcarpet (~> 3.6)
|
||||||
|
@ -863,10 +895,12 @@ DEPENDENCIES
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
rubocop-rails
|
rubocop-rails
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
|
ruby-prof
|
||||||
ruby-progressbar (~> 1.13)
|
ruby-progressbar (~> 1.13)
|
||||||
rubyzip (~> 2.3)
|
rubyzip (~> 2.3)
|
||||||
sanitize (~> 6.0)
|
sanitize (~> 6.0)
|
||||||
scenic (~> 1.7)
|
scenic (~> 1.7)
|
||||||
|
selenium-webdriver
|
||||||
sidekiq (~> 6.5)
|
sidekiq (~> 6.5)
|
||||||
sidekiq-bulk (~> 0.2.0)
|
sidekiq-bulk (~> 0.2.0)
|
||||||
sidekiq-scheduler (~> 5.0)
|
sidekiq-scheduler (~> 5.0)
|
||||||
|
@ -879,6 +913,7 @@ DEPENDENCIES
|
||||||
stackprof
|
stackprof
|
||||||
stoplight (~> 3.0.1)
|
stoplight (~> 3.0.1)
|
||||||
strong_migrations (~> 0.8)
|
strong_migrations (~> 0.8)
|
||||||
|
test-prof
|
||||||
thor (~> 1.2)
|
thor (~> 1.2)
|
||||||
tty-prompt (~> 0.23)
|
tty-prompt (~> 0.23)
|
||||||
twitter-text (~> 3.1.0)
|
twitter-text (~> 3.1.0)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
||||||
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
||||||
stream: env PORT=4000 yarn run start
|
stream: env PORT=4000 yarn run start
|
||||||
webpack: env RAILS_ENV=development NODE_ENV=development ./bin/webpack-dev-server --listen-host 0.0.0.0
|
webpack: env RAILS_ENV=development NODE_ENV=development NODE_OPTIONS=--openssl-legacy-provider ./bin/webpack-dev-server --listen-host 0.0.0.0
|
||||||
|
|
4
Rakefile
4
Rakefile
|
@ -1,7 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require File.expand_path('../config/application', __FILE__)
|
require File.expand_path('config/application', __dir__)
|
||||||
|
|
||||||
Rails.application.load_tasks
|
Rails.application.load_tasks
|
||||||
|
|
||||||
|
|
12
app/chewy/instances_index.rb
Normal file
12
app/chewy/instances_index.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class InstancesIndex < Chewy::Index
|
||||||
|
settings index: { refresh_interval: '30s' }
|
||||||
|
|
||||||
|
index_scope ::Instance.searchable
|
||||||
|
|
||||||
|
root date_detection: false do
|
||||||
|
field :domain, type: 'text', index_prefixes: { min_chars: 1 }
|
||||||
|
field :accounts_count, type: 'long'
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ class AccountsController < ApplicationController
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||||
|
|
||||||
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
|
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -65,7 +65,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_instances
|
def filtered_instances
|
||||||
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
|
InstanceFilter.new(limited_federation_mode? ? { allowed: true } : filter_params).results
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Api::BaseController < ApplicationController
|
||||||
include AccessTokenTrackingConcern
|
include AccessTokenTrackingConcern
|
||||||
include ApiCachingConcern
|
include ApiCachingConcern
|
||||||
|
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
|
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
|
||||||
before_action :require_not_suspended!
|
before_action :require_not_suspended!
|
||||||
|
@ -150,7 +150,7 @@ class Api::BaseController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def disallow_unauthenticated_api_access?
|
def disallow_unauthenticated_api_access?
|
||||||
ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] == 'true' || Rails.configuration.x.whitelist_mode
|
ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] == 'true' || Rails.configuration.x.limited_federation_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::BookmarksController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@_results ||= account_bookmarks.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
@results ||= account_bookmarks.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@_results ||= account_favourites.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
@results ||= account_favourites.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class Api::V1::Instances::ActivityController < Api::BaseController
|
class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
vary_by ''
|
vary_by ''
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.activity_api_enabled && !whitelist_mode?
|
head 404 unless Setting.activity_api_enabled && !limited_federation_mode?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Instances::DomainBlocksController < Api::BaseController
|
class Api::V1::Instances::DomainBlocksController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
before_action :set_domain_blocks
|
before_action :set_domain_blocks
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController
|
class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
skip_around_action :set_locale
|
skip_around_action :set_locale
|
||||||
|
|
||||||
before_action :set_extended_description
|
before_action :set_extended_description
|
||||||
|
@ -10,7 +10,7 @@ class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
||||||
def current_user
|
def current_user
|
||||||
super if whitelist_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
@ -3,24 +3,24 @@
|
||||||
class Api::V1::Instances::PeersController < Api::BaseController
|
class Api::V1::Instances::PeersController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
skip_around_action :set_locale
|
skip_around_action :set_locale
|
||||||
|
|
||||||
vary_by ''
|
vary_by ''
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
||||||
def current_user
|
def current_user
|
||||||
super if whitelist_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
cache_even_if_authenticated!
|
cache_even_if_authenticated!
|
||||||
render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
|
render_with_cache(expires_in: 1.day) { Instance.searchable.pluck(:domain) }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.peers_api_enabled && !whitelist_mode?
|
head 404 unless Setting.peers_api_enabled && !limited_federation_mode?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Instances::PrivacyPoliciesController < Api::BaseController
|
class Api::V1::Instances::PrivacyPoliciesController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
before_action :set_privacy_policy
|
before_action :set_privacy_policy
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Instances::RulesController < Api::BaseController
|
class Api::V1::Instances::RulesController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
skip_around_action :set_locale
|
skip_around_action :set_locale
|
||||||
|
|
||||||
before_action :set_rules
|
before_action :set_rules
|
||||||
|
@ -10,7 +10,7 @@ class Api::V1::Instances::RulesController < Api::BaseController
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
||||||
def current_user
|
def current_user
|
||||||
super if whitelist_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Instances::TranslationLanguagesController < Api::BaseController
|
class Api::V1::Instances::TranslationLanguagesController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
before_action :set_languages
|
before_action :set_languages
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::InstancesController < Api::BaseController
|
class Api::V1::InstancesController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
skip_around_action :set_locale
|
skip_around_action :set_locale
|
||||||
|
|
||||||
vary_by ''
|
vary_by ''
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
||||||
def current_user
|
def current_user
|
||||||
super if whitelist_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
@ -7,7 +7,10 @@ class Api::V1::MarkersController < Api::BaseController
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@markers = current_user.markers.where(timeline: Array(params[:timeline])).index_by(&:timeline)
|
with_read_replica do
|
||||||
|
@markers = current_user.markers.where(timeline: Array(params[:timeline])).index_by(&:timeline)
|
||||||
|
end
|
||||||
|
|
||||||
render json: serialize_map(@markers)
|
render json: serialize_map(@markers)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,12 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||||
DEFAULT_NOTIFICATIONS_LIMIT = 40
|
DEFAULT_NOTIFICATIONS_LIMIT = 40
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@notifications = load_notifications
|
with_read_replica do
|
||||||
render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
@notifications = load_notifications
|
||||||
|
@relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: @relationships
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
45
app/controllers/api/v1/peers/search_controller.rb
Normal file
45
app/controllers/api/v1/peers/search_controller.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Peers::SearchController < Api::BaseController
|
||||||
|
before_action :require_enabled_api!
|
||||||
|
before_action :set_domains
|
||||||
|
|
||||||
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
skip_around_action :set_locale
|
||||||
|
|
||||||
|
vary_by ''
|
||||||
|
|
||||||
|
def index
|
||||||
|
cache_even_if_authenticated!
|
||||||
|
render json: @domains
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def require_enabled_api!
|
||||||
|
head 404 unless Setting.peers_api_enabled && !limited_federation_mode?
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_domains
|
||||||
|
return if params[:q].blank?
|
||||||
|
|
||||||
|
if Chewy.enabled?
|
||||||
|
@domains = InstancesIndex.query(function_score: {
|
||||||
|
query: {
|
||||||
|
prefix: {
|
||||||
|
domain: TagManager.instance.normalize_domain(params[:q].strip),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
field_value_factor: {
|
||||||
|
field: 'accounts_count',
|
||||||
|
modifier: 'log2p',
|
||||||
|
},
|
||||||
|
}).limit(10).pluck(:domain)
|
||||||
|
else
|
||||||
|
domain = params[:q].strip
|
||||||
|
domain = TagManager.instance.normalize_domain(domain)
|
||||||
|
@domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,6 +23,6 @@ class Api::V1::ReportsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_params
|
def report_params
|
||||||
params.permit(:account_id, :comment, :category, :forward, status_ids: [], rule_ids: [])
|
params.permit(:account_id, :comment, :category, :forward, forward_to_domains: [], status_ids: [], rule_ids: [])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,13 +17,16 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
|
||||||
|
|
||||||
if fav
|
if fav
|
||||||
@status = fav.status
|
@status = fav.status
|
||||||
|
count = [@status.favourites_count - 1, 0].max
|
||||||
UnfavouriteWorker.perform_async(current_account.id, @status.id)
|
UnfavouriteWorker.perform_async(current_account.id, @status.id)
|
||||||
else
|
else
|
||||||
@status = Status.find(params[:status_id])
|
@status = Status.find(params[:status_id])
|
||||||
|
count = @status.favourites_count
|
||||||
authorize @status, :show?
|
authorize @status, :show?
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
|
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }, attributes_map: { @status.id => { favourites_count: count } })
|
||||||
|
render json: @status, serializer: REST::StatusSerializer, relationships: relationships
|
||||||
rescue Mastodon::NotPermittedError
|
rescue Mastodon::NotPermittedError
|
||||||
not_found
|
not_found
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,15 +24,18 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController
|
||||||
|
|
||||||
if @status
|
if @status
|
||||||
authorize @status, :unreblog?
|
authorize @status, :unreblog?
|
||||||
|
@reblog = @status.reblog
|
||||||
|
count = [@reblog.reblogs_count - 1, 0].max
|
||||||
@status.discard
|
@status.discard
|
||||||
RemovalWorker.perform_async(@status.id)
|
RemovalWorker.perform_async(@status.id)
|
||||||
@reblog = @status.reblog
|
|
||||||
else
|
else
|
||||||
@reblog = Status.find(params[:status_id])
|
@reblog = Status.find(params[:status_id])
|
||||||
|
count = @reblog.reblogs_count
|
||||||
authorize @reblog, :show?
|
authorize @reblog, :show?
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: @reblog, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false })
|
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }, attributes_map: { @reblog.id => { reblogs_count: count } })
|
||||||
|
render json: @reblog, serializer: REST::StatusSerializer, relationships: relationships
|
||||||
rescue Mastodon::NotPermittedError
|
rescue Mastodon::NotPermittedError
|
||||||
not_found
|
not_found
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Api::V1::TagsController < Api::BaseController
|
||||||
|
|
||||||
def unfollow
|
def unfollow
|
||||||
TagFollow.find_by(account: current_account, tag: @tag)&.destroy!
|
TagFollow.find_by(account: current_account, tag: @tag)&.destroy!
|
||||||
|
TagUnmergeWorker.perform_async(@tag.id, current_account.id)
|
||||||
render json: @tag, serializer: REST::TagSerializer
|
render json: @tag, serializer: REST::TagSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,14 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
with_read_replica do
|
||||||
|
@statuses = load_statuses
|
||||||
|
@relationships = StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
|
relationships: @relationships,
|
||||||
status: account_home_feed.regenerating? ? 206 : 200
|
status: account_home_feed.regenerating? ? 206 : 200
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Web::EmbedsController < Api::Web::BaseController
|
class Api::Web::EmbedsController < Api::Web::BaseController
|
||||||
before_action :require_user!
|
include Authorization
|
||||||
|
|
||||||
def create
|
before_action :set_status
|
||||||
status = StatusFinder.new(params[:url]).status
|
|
||||||
|
|
||||||
return not_found if status.hidden?
|
def show
|
||||||
|
return not_found if @status.hidden?
|
||||||
|
|
||||||
render json: status, serializer: OEmbedSerializer, width: 400
|
if @status.local?
|
||||||
rescue ActiveRecord::RecordNotFound
|
render json: @status, serializer: OEmbedSerializer, width: 400
|
||||||
oembed = FetchOEmbedService.new.call(params[:url])
|
else
|
||||||
|
return not_found unless user_signed_in?
|
||||||
|
|
||||||
return not_found if oembed.nil?
|
url = ActivityPub::TagManager.instance.url_for(@status)
|
||||||
|
oembed = FetchOEmbedService.new.call(url)
|
||||||
|
return not_found if oembed.nil?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
||||||
rescue ArgumentError
|
rescue ArgumentError
|
||||||
return not_found
|
return not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: oembed
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
render json: oembed
|
def set_status
|
||||||
|
@status = Status.find(params[:id])
|
||||||
|
authorize @status, :show?
|
||||||
|
rescue Mastodon::NotPermittedError
|
||||||
|
not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
|
||||||
include CacheConcern
|
include CacheConcern
|
||||||
include DomainControlHelper
|
include DomainControlHelper
|
||||||
include ThemingConcern
|
include ThemingConcern
|
||||||
|
include DatabaseHelper
|
||||||
|
|
||||||
helper_method :current_account
|
helper_method :current_account
|
||||||
helper_method :current_session
|
helper_method :current_session
|
||||||
|
@ -20,7 +21,7 @@ class ApplicationController < ActionController::Base
|
||||||
helper_method :use_seamless_external_login?
|
helper_method :use_seamless_external_login?
|
||||||
helper_method :omniauth_only?
|
helper_method :omniauth_only?
|
||||||
helper_method :sso_account_settings
|
helper_method :sso_account_settings
|
||||||
helper_method :whitelist_mode?
|
helper_method :limited_federation_mode?
|
||||||
helper_method :body_class_string
|
helper_method :body_class_string
|
||||||
helper_method :skip_csrf_meta_tags?
|
helper_method :skip_csrf_meta_tags?
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ class ApplicationController < ActionController::Base
|
||||||
private
|
private
|
||||||
|
|
||||||
def authorized_fetch_mode?
|
def authorized_fetch_mode?
|
||||||
ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.whitelist_mode
|
ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.limited_federation_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_fetch_mode?
|
def public_fetch_mode?
|
||||||
|
|
|
@ -5,21 +5,13 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
|
||||||
def self.provides_callback_for(provider)
|
def self.provides_callback_for(provider)
|
||||||
define_method provider do
|
define_method provider do
|
||||||
|
@provider = provider
|
||||||
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user)
|
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user)
|
||||||
|
|
||||||
if @user.persisted?
|
if @user.persisted?
|
||||||
LoginActivity.create(
|
record_login_activity
|
||||||
user: @user,
|
|
||||||
success: true,
|
|
||||||
authentication_method: :omniauth,
|
|
||||||
provider: provider,
|
|
||||||
ip: request.remote_ip,
|
|
||||||
user_agent: request.user_agent
|
|
||||||
)
|
|
||||||
|
|
||||||
sign_in_and_redirect @user, event: :authentication
|
sign_in_and_redirect @user, event: :authentication
|
||||||
label = Devise.omniauth_configs[provider]&.strategy&.display_name.presence || I18n.t("auth.providers.#{provider}", default: provider.to_s.chomp('_oauth2').capitalize)
|
set_flash_message(:notice, :success, kind: label_for_provider) if is_navigational_format?
|
||||||
set_flash_message(:notice, :success, kind: label) if is_navigational_format?
|
|
||||||
else
|
else
|
||||||
session["devise.#{provider}_data"] = request.env['omniauth.auth']
|
session["devise.#{provider}_data"] = request.env['omniauth.auth']
|
||||||
redirect_to new_user_registration_url
|
redirect_to new_user_registration_url
|
||||||
|
@ -38,4 +30,29 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
auth_setup_path(missing_email: '1')
|
auth_setup_path(missing_email: '1')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def record_login_activity
|
||||||
|
LoginActivity.create(
|
||||||
|
user: @user,
|
||||||
|
success: true,
|
||||||
|
authentication_method: :omniauth,
|
||||||
|
provider: @provider,
|
||||||
|
ip: request.remote_ip,
|
||||||
|
user_agent: request.user_agent
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def label_for_provider
|
||||||
|
provider_display_name || configured_provider_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def provider_display_name
|
||||||
|
Devise.omniauth_configs[@provider]&.strategy&.display_name.presence
|
||||||
|
end
|
||||||
|
|
||||||
|
def configured_provider_name
|
||||||
|
I18n.t("auth.providers.#{@provider}", default: @provider.to_s.chomp('_oauth2').capitalize)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -113,7 +113,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
end
|
end
|
||||||
|
|
||||||
def home_paths(resource)
|
def home_paths(resource)
|
||||||
paths = [about_path]
|
paths = [about_path, '/explore']
|
||||||
|
|
||||||
paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)
|
paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
redirect_to new_user_session_path, alert: I18n.t('devise.failure.timeout')
|
redirect_to new_user_session_path, alert: I18n.t('devise.failure.timeout')
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_attempt_session(user)
|
def register_attempt_in_session(user)
|
||||||
session[:attempt_user_id] = user.id
|
session[:attempt_user_id] = user.id
|
||||||
session[:attempt_user_updated_at] = user.updated_at.to_s
|
session[:attempt_user_updated_at] = user.updated_at.to_s
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,33 +3,19 @@
|
||||||
class AuthorizeInteractionsController < ApplicationController
|
class AuthorizeInteractionsController < ApplicationController
|
||||||
include Authorization
|
include Authorization
|
||||||
|
|
||||||
layout 'modal'
|
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_body_classes
|
|
||||||
before_action :set_resource
|
before_action :set_resource
|
||||||
before_action :set_pack
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
if @resource.is_a?(Account)
|
if @resource.is_a?(Account)
|
||||||
render :show
|
redirect_to web_url("@#{@resource.pretty_acct}")
|
||||||
elsif @resource.is_a?(Status)
|
elsif @resource.is_a?(Status)
|
||||||
redirect_to web_url("@#{@resource.account.pretty_acct}/#{@resource.id}")
|
redirect_to web_url("@#{@resource.account.pretty_acct}/#{@resource.id}")
|
||||||
else
|
else
|
||||||
render :error
|
not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
|
||||||
if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource, with_rate_limit: true)
|
|
||||||
render :success
|
|
||||||
else
|
|
||||||
render :error
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render :error
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_resource
|
def set_resource
|
||||||
|
@ -62,12 +48,4 @@ class AuthorizeInteractionsController < ApplicationController
|
||||||
def uri_param
|
def uri_param
|
||||||
params[:uri] || params.fetch(:acct, '').delete_prefix('acct:')
|
params[:uri] || params.fetch(:acct, '').delete_prefix('acct:')
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_body_classes
|
|
||||||
@body_classes = 'modal-layout'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_pack
|
|
||||||
use_pack 'modal'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ class BackupsController < ApplicationController
|
||||||
|
|
||||||
def download
|
def download
|
||||||
case Paperclip::Attachment.default_options[:storage]
|
case Paperclip::Attachment.default_options[:storage]
|
||||||
when :s3
|
when :s3, :azure
|
||||||
redirect_to @backup.dump.expiring_url(10), allow_other_host: true
|
redirect_to @backup.dump.expiring_url(10), allow_other_host: true
|
||||||
when :fog
|
when :fog
|
||||||
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
||||||
|
|
|
@ -4,7 +4,7 @@ module AccountOwnedConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
before_action :authenticate_user!, if: -> { whitelist_mode? && request.format != :json }
|
before_action :authenticate_user!, if: -> { limited_federation_mode? && request.format != :json }
|
||||||
before_action :set_account, if: :account_required?
|
before_action :set_account, if: :account_required?
|
||||||
before_action :check_account_approval, if: :account_required?
|
before_action :check_account_approval, if: :account_required?
|
||||||
before_action :check_account_suspension, if: :account_required?
|
before_action :check_account_suspension, if: :account_required?
|
||||||
|
|
|
@ -8,6 +8,6 @@ module ApiCachingConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_even_if_authenticated!
|
def cache_even_if_authenticated!
|
||||||
expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless whitelist_mode?
|
expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless limited_federation_mode?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,7 +61,7 @@ module RateLimitHeaders
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_time
|
def request_time
|
||||||
@_request_time ||= Time.now.utc
|
@request_time ||= Time.now.utc
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_period_offset
|
def reset_period_offset
|
||||||
|
|
|
@ -75,7 +75,7 @@ module TwoFactorAuthenticationConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def prompt_for_two_factor(user)
|
def prompt_for_two_factor(user)
|
||||||
set_attempt_session(user)
|
register_attempt_in_session(user)
|
||||||
|
|
||||||
use_pack 'auth'
|
use_pack 'auth'
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class FollowerAccountsController < ApplicationController
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||||
|
|
||||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
def index
|
def index
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class FollowingAccountsController < ApplicationController
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||||
|
|
||||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
def index
|
def index
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -9,6 +9,8 @@ class MailSubscriptionsController < ApplicationController
|
||||||
before_action :set_user
|
before_action :set_user
|
||||||
before_action :set_type
|
before_action :set_type
|
||||||
|
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -20,6 +22,7 @@ class MailSubscriptionsController < ApplicationController
|
||||||
|
|
||||||
def set_user
|
def set_user
|
||||||
@user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
@user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
||||||
|
not_found unless @user
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_body_classes
|
def set_body_classes
|
||||||
|
@ -35,7 +38,7 @@ class MailSubscriptionsController < ApplicationController
|
||||||
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
||||||
"notification_emails.#{params[:type]}"
|
"notification_emails.#{params[:type]}"
|
||||||
else
|
else
|
||||||
raise ArgumentError
|
not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
class MediaController < ApplicationController
|
class MediaController < ApplicationController
|
||||||
include Authorization
|
include Authorization
|
||||||
|
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
before_action :authenticate_user!, if: :whitelist_mode?
|
before_action :authenticate_user!, if: :limited_federation_mode?
|
||||||
before_action :set_media_attachment
|
before_action :set_media_attachment
|
||||||
before_action :verify_permitted_status!
|
before_action :verify_permitted_status!
|
||||||
before_action :check_playable, only: :player
|
before_action :check_playable, only: :player
|
||||||
|
|
|
@ -8,7 +8,7 @@ class MediaProxyController < ApplicationController
|
||||||
|
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :authenticate_user!, if: :whitelist_mode?
|
before_action :authenticate_user!, if: :limited_federation_mode?
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordInvalid, with: :not_found
|
rescue_from ActiveRecord::RecordInvalid, with: :not_found
|
||||||
rescue_from Mastodon::UnexpectedResponseError, with: :not_found
|
rescue_from Mastodon::UnexpectedResponseError, with: :not_found
|
||||||
|
|
43
app/controllers/remote_interaction_helper_controller.rb
Normal file
43
app/controllers/remote_interaction_helper_controller.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoteInteractionHelperController < ApplicationController
|
||||||
|
vary_by ''
|
||||||
|
|
||||||
|
skip_before_action :require_functional!
|
||||||
|
skip_around_action :set_locale
|
||||||
|
skip_before_action :update_user_sign_in
|
||||||
|
|
||||||
|
content_security_policy do |p|
|
||||||
|
# We inherit the normal `script-src`
|
||||||
|
|
||||||
|
# Set every directive that does not have a fallback
|
||||||
|
p.default_src :none
|
||||||
|
p.form_action :none
|
||||||
|
p.base_uri :none
|
||||||
|
|
||||||
|
# Disable every directive with a fallback to cut on response size
|
||||||
|
p.base_uri false
|
||||||
|
p.font_src false
|
||||||
|
p.img_src false
|
||||||
|
p.style_src false
|
||||||
|
p.media_src false
|
||||||
|
p.frame_src false
|
||||||
|
p.manifest_src false
|
||||||
|
p.connect_src false
|
||||||
|
p.child_src false
|
||||||
|
p.worker_src false
|
||||||
|
|
||||||
|
# Widen the directives that we do need
|
||||||
|
p.frame_ancestors :self
|
||||||
|
p.connect_src :https
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day)
|
||||||
|
|
||||||
|
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||||
|
response.headers['Referrer-Policy'] = 'no-referrer'
|
||||||
|
|
||||||
|
render layout: 'helper_frame'
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,7 +17,7 @@ class StatusesController < ApplicationController
|
||||||
after_action :set_link_headers
|
after_action :set_link_headers
|
||||||
|
|
||||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||||
skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode?
|
skip_before_action :require_functional!, only: [:show, :embed], unless: :limited_federation_mode?
|
||||||
|
|
||||||
content_security_policy only: :embed do |policy|
|
content_security_policy only: :embed do |policy|
|
||||||
policy.frame_ancestors(false)
|
policy.frame_ancestors(false)
|
||||||
|
|
|
@ -10,13 +10,13 @@ class TagsController < ApplicationController
|
||||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||||
|
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||||
before_action :authenticate_user!, if: :whitelist_mode?
|
before_action :authenticate_user!, if: :limited_federation_mode?
|
||||||
before_action :set_local
|
before_action :set_local
|
||||||
before_action :set_tag
|
before_action :set_tag
|
||||||
before_action :set_statuses, if: -> { request.format == :rss }
|
before_action :set_statuses, if: -> { request.format == :rss }
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
|
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||||
|
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -19,6 +19,7 @@ module WellKnown
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
username = username_from_resource
|
username = username_from_resource
|
||||||
|
|
||||||
@account = begin
|
@account = begin
|
||||||
if username == Rails.configuration.x.local_domain
|
if username == Rails.configuration.x.local_domain
|
||||||
Account.representative
|
Account.representative
|
||||||
|
|
|
@ -236,6 +236,6 @@ module ApplicationHelper
|
||||||
private
|
private
|
||||||
|
|
||||||
def storage_host_var
|
def storage_host_var
|
||||||
ENV.fetch('S3_ALIAS_HOST', nil) || ENV.fetch('S3_CLOUDFRONT_HOST', nil)
|
ENV.fetch('S3_ALIAS_HOST', nil) || ENV.fetch('S3_CLOUDFRONT_HOST', nil) || ENV.fetch('AZURE_ALIAS_HOST', nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,7 +22,14 @@ module ContextHelper
|
||||||
blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
|
blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
|
||||||
discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
|
discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
|
||||||
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
|
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
|
||||||
olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' },
|
olm: {
|
||||||
|
'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId',
|
||||||
|
'claim' => { '@type' => '@id', '@id' => 'toot:claim' },
|
||||||
|
'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' },
|
||||||
|
'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' },
|
||||||
|
'devices' => { '@type' => '@id', '@id' => 'toot:devices' },
|
||||||
|
'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText'
|
||||||
|
},
|
||||||
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
||||||
quote_uri: { 'fedibird' => 'http://fedibird.com/ns#', 'quoteUri' => 'fedibird:quoteUri' },
|
quote_uri: { 'fedibird' => 'http://fedibird.com/ns#', 'quoteUri' => 'fedibird:quoteUri' },
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
11
app/helpers/database_helper.rb
Normal file
11
app/helpers/database_helper.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module DatabaseHelper
|
||||||
|
def with_read_replica(&block)
|
||||||
|
ApplicationRecord.connected_to(role: :reading, prevent_writes: true, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_primary(&block)
|
||||||
|
ApplicationRecord.connected_to(role: :writing, &block)
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module DomainControlHelper
|
module DomainControlHelper
|
||||||
def domain_not_allowed?(uri_or_domain)
|
def domain_not_allowed?(uri_or_domain)
|
||||||
return if uri_or_domain.blank?
|
return false if uri_or_domain.blank?
|
||||||
|
|
||||||
domain = if uri_or_domain.include?('://')
|
domain = if uri_or_domain.include?('://')
|
||||||
Addressable::URI.parse(uri_or_domain).host
|
Addressable::URI.parse(uri_or_domain).host
|
||||||
|
@ -10,14 +10,14 @@ module DomainControlHelper
|
||||||
uri_or_domain
|
uri_or_domain
|
||||||
end
|
end
|
||||||
|
|
||||||
if whitelist_mode?
|
if limited_federation_mode?
|
||||||
!DomainAllow.allowed?(domain)
|
!DomainAllow.allowed?(domain)
|
||||||
else
|
else
|
||||||
DomainBlock.blocked?(domain)
|
DomainBlock.blocked?(domain)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def whitelist_mode?
|
def limited_federation_mode?
|
||||||
Rails.configuration.x.whitelist_mode
|
Rails.configuration.x.limited_federation_mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -204,7 +204,17 @@ module LanguagesHelper
|
||||||
zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze,
|
zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
SUPPORTED_LOCALES = {}.merge(ISO_639_1).merge(ISO_639_3).freeze
|
# e.g. For Chinese, which is not a language,
|
||||||
|
# but a language family in spite of sharing the main locale code
|
||||||
|
# We need to be able to filter these
|
||||||
|
ISO_639_1_REGIONAL = {
|
||||||
|
'zh-CN': ['Chinese (China)', '简体中文'].freeze,
|
||||||
|
'zh-HK': ['Chinese (Hong Kong)', '繁體中文(香港)'].freeze,
|
||||||
|
'zh-TW': ['Chinese (Taiwan)', '繁體中文(臺灣)'].freeze,
|
||||||
|
'zh-YUE': ['Cantonese', '廣東話'].freeze,
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
SUPPORTED_LOCALES = {}.merge(ISO_639_1).merge(ISO_639_1_REGIONAL).merge(ISO_639_3).freeze
|
||||||
|
|
||||||
# For ISO-639-1 and ISO-639-3 language codes, we have their official
|
# For ISO-639-1 and ISO-639-3 language codes, we have their official
|
||||||
# names, but for some translations, we need the names of the
|
# names, but for some translations, we need the names of the
|
||||||
|
@ -217,9 +227,6 @@ module LanguagesHelper
|
||||||
'pt-BR': 'Português (Brasil)',
|
'pt-BR': 'Português (Brasil)',
|
||||||
'pt-PT': 'Português (Portugal)',
|
'pt-PT': 'Português (Portugal)',
|
||||||
'sr-Latn': 'Srpski (latinica)',
|
'sr-Latn': 'Srpski (latinica)',
|
||||||
'zh-CN': '简体中文',
|
|
||||||
'zh-HK': '繁體中文(香港)',
|
|
||||||
'zh-TW': '繁體中文(臺灣)',
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def native_locale_name(locale)
|
def native_locale_name(locale)
|
||||||
|
|
|
@ -65,33 +65,6 @@ module StatusesHelper
|
||||||
embedded_view? ? '_blank' : nil
|
embedded_view? ? '_blank' : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def style_classes(status, is_predecessor, is_successor, include_threads)
|
|
||||||
classes = ['entry']
|
|
||||||
classes << 'entry-predecessor' if is_predecessor
|
|
||||||
classes << 'entry-reblog' if status.reblog?
|
|
||||||
classes << 'entry-successor' if is_successor
|
|
||||||
classes << 'entry-center' if include_threads
|
|
||||||
classes.join(' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def microformats_classes(status, is_direct_parent, is_direct_child)
|
|
||||||
classes = []
|
|
||||||
classes << 'p-in-reply-to' if is_direct_parent
|
|
||||||
classes << 'p-repost-of' if status.reblog? && is_direct_parent
|
|
||||||
classes << 'p-comment' if is_direct_child
|
|
||||||
classes.join(' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def microformats_h_class(status, is_predecessor, is_successor, include_threads)
|
|
||||||
if is_predecessor || status.reblog? || is_successor
|
|
||||||
'h-cite'
|
|
||||||
elsif include_threads
|
|
||||||
''
|
|
||||||
else
|
|
||||||
'h-entry'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fa_visibility_icon(status)
|
def fa_visibility_icon(status)
|
||||||
case status.visibility
|
case status.visibility
|
||||||
when 'public'
|
when 'public'
|
||||||
|
|
172
app/javascript/core/remote_interaction_helper.ts
Normal file
172
app/javascript/core/remote_interaction_helper.ts
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This script is meant to to be used in an `iframe` with the sole purpose of doing webfinger queries
|
||||||
|
client-side without being restricted by a strict `connect-src` Content-Security-Policy directive.
|
||||||
|
|
||||||
|
It communicates with the parent window through message events that are authenticated by origin,
|
||||||
|
and performs no other task.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'packs/public-path';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
interface JRDLink {
|
||||||
|
rel: string;
|
||||||
|
template?: string;
|
||||||
|
href?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isJRDLink = (link: unknown): link is JRDLink =>
|
||||||
|
typeof link === 'object' &&
|
||||||
|
link !== null &&
|
||||||
|
'rel' in link &&
|
||||||
|
typeof link.rel === 'string' &&
|
||||||
|
(!('template' in link) || typeof link.template === 'string') &&
|
||||||
|
(!('href' in link) || typeof link.href === 'string');
|
||||||
|
|
||||||
|
const findLink = (rel: string, data: unknown): JRDLink | undefined => {
|
||||||
|
if (
|
||||||
|
typeof data === 'object' &&
|
||||||
|
data !== null &&
|
||||||
|
'links' in data &&
|
||||||
|
data.links instanceof Array
|
||||||
|
) {
|
||||||
|
return data.links.find(
|
||||||
|
(link): link is JRDLink => isJRDLink(link) && link.rel === rel,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findTemplateLink = (data: unknown) =>
|
||||||
|
findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template;
|
||||||
|
|
||||||
|
const fetchInteractionURLSuccess = (
|
||||||
|
uri_or_domain: string,
|
||||||
|
template: string,
|
||||||
|
) => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
type: 'fetchInteractionURL-success',
|
||||||
|
uri_or_domain,
|
||||||
|
template,
|
||||||
|
},
|
||||||
|
window.origin,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchInteractionURLFailure = () => {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
type: 'fetchInteractionURL-failure',
|
||||||
|
},
|
||||||
|
window.origin,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidDomain = (value: string) => {
|
||||||
|
const url = new URL('https:///path');
|
||||||
|
url.hostname = value;
|
||||||
|
return url.hostname === value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt to find a remote interaction URL from a domain
|
||||||
|
const fromDomain = (domain: string) => {
|
||||||
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
|
axios
|
||||||
|
.get(`https://${domain}/.well-known/webfinger`, {
|
||||||
|
params: { resource: `https://${domain}` },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
const template = findTemplateLink(data);
|
||||||
|
fetchInteractionURLSuccess(domain, template ?? fallbackTemplate);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fetchInteractionURLSuccess(domain, fallbackTemplate);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt to find a remote interaction URL from an arbitrary URL
|
||||||
|
const fromURL = (url: string) => {
|
||||||
|
const domain = new URL(url).host;
|
||||||
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
|
axios
|
||||||
|
.get(`https://${domain}/.well-known/webfinger`, {
|
||||||
|
params: { resource: url },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
const template = findTemplateLink(data);
|
||||||
|
fetchInteractionURLSuccess(url, template ?? fallbackTemplate);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fromDomain(domain);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt to find a remote interaction URL from a `user@domain` string
|
||||||
|
const fromAcct = (acct: string) => {
|
||||||
|
acct = acct.replace(/^@/, '');
|
||||||
|
|
||||||
|
const segments = acct.split('@');
|
||||||
|
|
||||||
|
if (segments.length !== 2 || !segments[0] || !isValidDomain(segments[1])) {
|
||||||
|
fetchInteractionURLFailure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domain = segments[1];
|
||||||
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
|
axios
|
||||||
|
.get(`https://${domain}/.well-known/webfinger`, {
|
||||||
|
params: { resource: `acct:${acct}` },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
const template = findTemplateLink(data);
|
||||||
|
fetchInteractionURLSuccess(acct, template ?? fallbackTemplate);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// TODO: handle host-meta?
|
||||||
|
fromDomain(domain);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchInteractionURL = (uri_or_domain: string) => {
|
||||||
|
if (/^https?:\/\//.test(uri_or_domain)) {
|
||||||
|
fromURL(uri_or_domain);
|
||||||
|
} else if (uri_or_domain.includes('@')) {
|
||||||
|
fromAcct(uri_or_domain);
|
||||||
|
} else {
|
||||||
|
fromDomain(uri_or_domain);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', (event: MessageEvent<unknown>) => {
|
||||||
|
// Check message origin
|
||||||
|
if (
|
||||||
|
!window.origin ||
|
||||||
|
window.parent !== event.source ||
|
||||||
|
event.origin !== window.origin
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.data &&
|
||||||
|
typeof event.data === 'object' &&
|
||||||
|
'type' in event.data &&
|
||||||
|
event.data.type === 'fetchInteractionURL' &&
|
||||||
|
'uri_or_domain' in event.data &&
|
||||||
|
typeof event.data.uri_or_domain === 'string'
|
||||||
|
) {
|
||||||
|
fetchInteractionURL(event.data.uri_or_domain);
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,3 +18,4 @@ pack:
|
||||||
settings: settings.js
|
settings: settings.js
|
||||||
sign_up:
|
sign_up:
|
||||||
share:
|
share:
|
||||||
|
remote_interaction_helper: remote_interaction_helper.ts
|
||||||
|
|
|
@ -81,7 +81,7 @@ export function importFetchedStatuses(statuses) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.poll && status.poll.id) {
|
if (status.poll && status.poll.id) {
|
||||||
pushUnique(polls, normalizePoll(status.poll));
|
pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export function importFetchedStatuses(statuses) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importFetchedPoll(poll) {
|
export function importFetchedPoll(poll) {
|
||||||
return dispatch => {
|
return (dispatch, getState) => {
|
||||||
dispatch(importPolls([normalizePoll(poll)]));
|
dispatch(importPolls([normalizePoll(poll, getState().getIn(['polls', poll.id]))]));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ export function normalizeStatus(status, normalOldStatus, settings) {
|
||||||
normalStatus.hidden = normalOldStatus.get('hidden');
|
normalStatus.hidden = normalOldStatus.get('hidden');
|
||||||
normalStatus.quote = normalOldStatus.get('quote');
|
normalStatus.quote = normalOldStatus.get('quote');
|
||||||
normalStatus.quote_hidden = normalOldStatus.get('quote_hidden');
|
normalStatus.quote_hidden = normalOldStatus.get('quote_hidden');
|
||||||
|
normalStatus.translation = normalOldStatus.get('translation');
|
||||||
} else {
|
} else {
|
||||||
const spoilerText = normalStatus.spoiler_text || '';
|
const spoilerText = normalStatus.spoiler_text || '';
|
||||||
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||||
|
@ -117,6 +118,18 @@ export function normalizeStatus(status, normalOldStatus, settings) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalOldStatus) {
|
||||||
|
const list = normalOldStatus.get('media_attachments');
|
||||||
|
if (normalStatus.media_attachments && list) {
|
||||||
|
normalStatus.media_attachments.forEach(item => {
|
||||||
|
const oldItem = list.find(i => i.get('id') === item.id);
|
||||||
|
if (oldItem && oldItem.get('description') === item.description) {
|
||||||
|
item.translation = oldItem.get('translation')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return normalStatus;
|
return normalStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,15 +148,23 @@ export function normalizeStatusTranslation(translation, status) {
|
||||||
return normalTranslation;
|
return normalTranslation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizePoll(poll) {
|
export function normalizePoll(poll, normalOldPoll) {
|
||||||
const normalPoll = { ...poll };
|
const normalPoll = { ...poll };
|
||||||
const emojiMap = makeEmojiMap(poll.emojis);
|
const emojiMap = makeEmojiMap(poll.emojis);
|
||||||
|
|
||||||
normalPoll.options = poll.options.map((option, index) => ({
|
normalPoll.options = poll.options.map((option, index) => {
|
||||||
...option,
|
const normalOption = {
|
||||||
voted: poll.own_votes && poll.own_votes.includes(index),
|
...option,
|
||||||
titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
voted: poll.own_votes && poll.own_votes.includes(index),
|
||||||
}));
|
titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalOldPoll && normalOldPoll.getIn(['options', index, 'title']) === option.title) {
|
||||||
|
normalOption.translation = normalOldPoll.getIn(['options', index, 'translation']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalOption
|
||||||
|
});
|
||||||
|
|
||||||
return normalPoll;
|
return normalPoll;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
|
||||||
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
|
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
|
||||||
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
|
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
|
||||||
|
|
||||||
|
export const SEARCH_RESULT_CLICK = 'SEARCH_RESULT_CLICK';
|
||||||
|
export const SEARCH_RESULT_FORGET = 'SEARCH_RESULT_FORGET';
|
||||||
|
|
||||||
export function changeSearch(value) {
|
export function changeSearch(value) {
|
||||||
return {
|
return {
|
||||||
type: SEARCH_CHANGE,
|
type: SEARCH_CHANGE,
|
||||||
|
@ -28,7 +31,7 @@ export function clearSearch() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function submitSearch() {
|
export function submitSearch(type) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const value = getState().getIn(['search', 'value']);
|
const value = getState().getIn(['search', 'value']);
|
||||||
const signedIn = !!getState().getIn(['meta', 'me']);
|
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||||
|
@ -45,6 +48,7 @@ export function submitSearch() {
|
||||||
q: value,
|
q: value,
|
||||||
resolve: signedIn,
|
resolve: signedIn,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
type,
|
||||||
},
|
},
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (response.data.accounts) {
|
if (response.data.accounts) {
|
||||||
|
@ -131,3 +135,42 @@ export const expandSearchFail = error => ({
|
||||||
export const showSearch = () => ({
|
export const showSearch = () => ({
|
||||||
type: SEARCH_SHOW,
|
type: SEARCH_SHOW,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const openURL = routerHistory => (dispatch, getState) => {
|
||||||
|
const value = getState().getIn(['search', 'value']);
|
||||||
|
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||||
|
|
||||||
|
if (!signedIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchSearchRequest());
|
||||||
|
|
||||||
|
api(getState).get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
|
||||||
|
if (response.data.accounts?.length > 0) {
|
||||||
|
dispatch(importFetchedAccounts(response.data.accounts));
|
||||||
|
routerHistory.push(`/@${response.data.accounts[0].acct}`);
|
||||||
|
} else if (response.data.statuses?.length > 0) {
|
||||||
|
dispatch(importFetchedStatuses(response.data.statuses));
|
||||||
|
routerHistory.push(`/@${response.data.statuses[0].account.acct}/${response.data.statuses[0].id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchSearchSuccess(response.data, value));
|
||||||
|
}).catch(err => {
|
||||||
|
dispatch(fetchSearchFail(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clickSearchResult = (q, type) => ({
|
||||||
|
type: SEARCH_RESULT_CLICK,
|
||||||
|
|
||||||
|
result: {
|
||||||
|
type,
|
||||||
|
q,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const forgetSearchResult = q => ({
|
||||||
|
type: SEARCH_RESULT_FORGET,
|
||||||
|
q,
|
||||||
|
});
|
||||||
|
|
|
@ -86,10 +86,9 @@ const DIGIT_CHARACTERS = [
|
||||||
|
|
||||||
export const decode83 = (str: string) => {
|
export const decode83 = (str: string) => {
|
||||||
let value = 0;
|
let value = 0;
|
||||||
let c, digit;
|
let digit;
|
||||||
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
for (const c of str) {
|
||||||
c = str[i];
|
|
||||||
digit = DIGIT_CHARACTERS.indexOf(c);
|
digit = DIGIT_CHARACTERS.indexOf(c);
|
||||||
value = value * 83 + digit;
|
value = value * 83 + digit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { TransitionMotion, spring } from 'react-motion';
|
||||||
|
|
||||||
import { reduceMotion } from '../initial_state';
|
import { reduceMotion } from '../initial_state';
|
||||||
|
|
||||||
import ShortNumber from './short_number';
|
import { ShortNumber } from './short_number';
|
||||||
|
|
||||||
const obfuscatedCount = (count: number) => {
|
const obfuscatedCount = (count: number) => {
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
|
@ -33,7 +33,7 @@ export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
|
||||||
const willEnter = useCallback(() => ({ y: -1 * direction }), [direction]);
|
const willEnter = useCallback(() => ({ y: -1 * direction }), [direction]);
|
||||||
const willLeave = useCallback(
|
const willLeave = useCallback(
|
||||||
() => ({ y: spring(1 * direction, { damping: 35, stiffness: 400 }) }),
|
() => ({ y: spring(1 * direction, { damping: 35, stiffness: 400 }) }),
|
||||||
[direction]
|
[direction],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (reduceMotion) {
|
if (reduceMotion) {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ShortNumber from 'flavours/glitch/components/short_number';
|
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tag: {
|
tag: {
|
||||||
name: string;
|
name: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
history?: Array<{
|
history?: {
|
||||||
uses: number;
|
uses: number;
|
||||||
accounts: string;
|
accounts: string;
|
||||||
day: string;
|
day: string;
|
||||||
}>;
|
}[];
|
||||||
following?: boolean;
|
following?: boolean;
|
||||||
type: 'hashtag';
|
type: 'hashtag';
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const Avatar: React.FC<Props> = ({
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
style.backgroundImage = `url(${account.get(
|
style.backgroundImage = `url(${account.get(
|
||||||
hovering ? 'avatar' : 'avatar_static'
|
hovering ? 'avatar' : 'avatar_static',
|
||||||
)})`;
|
)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ export const Avatar: React.FC<Props> = ({
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'account__avatar',
|
'account__avatar',
|
||||||
{ 'account__avatar-inline': inline },
|
{ 'account__avatar-inline': inline },
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
|
|
|
@ -20,9 +20,7 @@ export default class ColumnBackButton extends PureComponent {
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
|
|
||||||
// Check if there is a previous page in the app to go back to per https://stackoverflow.com/a/70532858/9703201
|
if (router.history.location?.state?.fromMastodon) {
|
||||||
// When upgrading to V6, check `location.key !== 'default'` instead per https://github.com/remix-run/history/blob/main/docs/api-reference.md#location
|
|
||||||
if (router.route.location.key) {
|
|
||||||
router.history.goBack();
|
router.history.goBack();
|
||||||
} else {
|
} else {
|
||||||
router.history.push('/');
|
router.history.push('/');
|
||||||
|
|
|
@ -65,9 +65,7 @@ class ColumnHeader extends PureComponent {
|
||||||
handleBackClick = () => {
|
handleBackClick = () => {
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
|
|
||||||
// Check if there is a previous page in the app to go back to per https://stackoverflow.com/a/70532858/9703201
|
if (router.history.location?.state?.fromMastodon) {
|
||||||
// When upgrading to V6, check `location.key !== 'default'` instead per https://github.com/remix-run/history/blob/main/docs/api-reference.md#location
|
|
||||||
if (router.route.location.key) {
|
|
||||||
router.history.goBack();
|
router.history.goBack();
|
||||||
} else {
|
} else {
|
||||||
router.history.push('/');
|
router.history.push('/');
|
||||||
|
@ -87,6 +85,7 @@ class ColumnHeader extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { router } = this.context;
|
||||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
|
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
|
||||||
const { collapsed, animating } = this.state;
|
const { collapsed, animating } = this.state;
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ class ColumnHeader extends PureComponent {
|
||||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pinned && (multiColumn || showBackButton)) {
|
if (!pinned && ((multiColumn && router.history.location?.state?.fromMastodon) || showBackButton)) {
|
||||||
backButton = (
|
backButton = (
|
||||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
||||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
/**
|
|
||||||
* Returns custom renderer for one of the common counter types
|
|
||||||
* @param {"statuses" | "following" | "followers"} counterType
|
|
||||||
* Type of the counter
|
|
||||||
* @param {boolean} isBold Whether display number must be displayed in bold
|
|
||||||
* @returns {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element}
|
|
||||||
* Renderer function
|
|
||||||
* @throws If counterType is not covered by this function
|
|
||||||
*/
|
|
||||||
export function counterRenderer(counterType, isBold = true) {
|
|
||||||
/**
|
|
||||||
* @type {(displayNumber: JSX.Element) => JSX.Element}
|
|
||||||
*/
|
|
||||||
const renderCounter = isBold
|
|
||||||
? (displayNumber) => <strong>{displayNumber}</strong>
|
|
||||||
: (displayNumber) => displayNumber;
|
|
||||||
|
|
||||||
switch (counterType) {
|
|
||||||
case 'statuses': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.statuses_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'following': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.following_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'followers': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.followers_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: throw Error(`Incorrect counter name: ${counterType}. Ensure it accepted by commonCounter function`);
|
|
||||||
}
|
|
||||||
}
|
|
45
app/javascript/flavours/glitch/components/counters.tsx
Normal file
45
app/javascript/flavours/glitch/components/counters.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
export const StatusesCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number,
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.statuses_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FollowingCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number,
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.following_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FollowersCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number,
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.followers_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
|
@ -1,55 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
|
||||||
|
|
||||||
import { bannerSettings } from 'flavours/glitch/settings';
|
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
|
||||||
});
|
|
||||||
|
|
||||||
class DismissableBanner extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
visible: !bannerSettings.get(this.props.id),
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDismiss = () => {
|
|
||||||
const { id } = this.props;
|
|
||||||
this.setState({ visible: false }, () => bannerSettings.set(id, true));
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { visible } = this.state;
|
|
||||||
|
|
||||||
if (!visible) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { children, intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='dismissable-banner'>
|
|
||||||
<div className='dismissable-banner__message'>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='dismissable-banner__action'>
|
|
||||||
<IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(DismissableBanner);
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { bannerSettings } from 'flavours/glitch/settings';
|
||||||
|
|
||||||
|
import { IconButton } from './icon_button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
||||||
|
id,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [visible, setVisible] = useState(!bannerSettings.get(id));
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleDismiss = useCallback(() => {
|
||||||
|
setVisible(false);
|
||||||
|
bannerSettings.set(id, true);
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='dismissable-banner'>
|
||||||
|
<div className='dismissable-banner__message'>{children}</div>
|
||||||
|
|
||||||
|
<div className='dismissable-banner__action'>
|
||||||
|
<IconButton
|
||||||
|
icon='times'
|
||||||
|
title={intl.formatMessage(messages.dismiss)}
|
||||||
|
onClick={handleDismiss}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -11,11 +11,12 @@ import { autoPlayGif } from '../initial_state';
|
||||||
import { Skeleton } from './skeleton';
|
import { Skeleton } from './skeleton';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
account: Account;
|
account?: Account;
|
||||||
others: List<Account>;
|
others?: List<Account>;
|
||||||
localDomain: string;
|
localDomain?: string;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DisplayName extends React.PureComponent<Props> {
|
export class DisplayName extends React.PureComponent<Props> {
|
||||||
handleMouseEnter: React.ReactEventHandler<HTMLSpanElement> = ({
|
handleMouseEnter: React.ReactEventHandler<HTMLSpanElement> = ({
|
||||||
currentTarget,
|
currentTarget,
|
||||||
|
@ -52,7 +53,15 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { others, localDomain, inline } = this.props;
|
const { others, localDomain, inline } = this.props;
|
||||||
|
|
||||||
let displayName: React.ReactNode, suffix: React.ReactNode, account: Account;
|
let displayName: React.ReactNode,
|
||||||
|
suffix: React.ReactNode,
|
||||||
|
account: Account | undefined;
|
||||||
|
|
||||||
|
if (others && others.size > 0) {
|
||||||
|
account = others.first();
|
||||||
|
} else if (this.props.account) {
|
||||||
|
account = this.props.account;
|
||||||
|
}
|
||||||
|
|
||||||
if (others && others.size > 1) {
|
if (others && others.size > 1) {
|
||||||
displayName = others
|
displayName = others
|
||||||
|
@ -70,16 +79,10 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||||
if (others.size - 2 > 0) {
|
if (others.size - 2 > 0) {
|
||||||
suffix = `+${others.size - 2}`;
|
suffix = `+${others.size - 2}`;
|
||||||
}
|
}
|
||||||
} else if ((others && others.size > 0) || this.props.account) {
|
} else if (account) {
|
||||||
if (others && others.size > 0) {
|
|
||||||
account = others.first();
|
|
||||||
} else {
|
|
||||||
account = this.props.account;
|
|
||||||
}
|
|
||||||
|
|
||||||
let acct = account.get('acct');
|
let acct = account.get('acct');
|
||||||
|
|
||||||
if (acct.indexOf('@') === -1 && localDomain) {
|
if (!acct.includes('@') && localDomain) {
|
||||||
acct = `${acct}@${localDomain}`;
|
acct = `${acct}@${localDomain}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@ import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import StackTrace from 'stacktrace-js';
|
import StackTrace from 'stacktrace-js';
|
||||||
|
|
||||||
import { source_url } from 'flavours/glitch/initial_state';
|
import { version, source_url } from 'flavours/glitch/initial_state';
|
||||||
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
|
||||||
|
|
||||||
export default class ErrorBoundary extends PureComponent {
|
export default class ErrorBoundary extends PureComponent {
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ export default class ErrorBoundary extends PureComponent {
|
||||||
componentStack: undefined,
|
componentStack: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidCatch(error, info) {
|
componentDidCatch (error, info) {
|
||||||
this.setState({
|
this.setState({
|
||||||
hasError: true,
|
hasError: true,
|
||||||
errorMessage: error.toString(),
|
errorMessage: error.toString(),
|
||||||
|
@ -44,88 +43,62 @@ export default class ErrorBoundary extends PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReload(e) {
|
handleCopyStackTrace = () => {
|
||||||
e.preventDefault();
|
const { errorMessage, stackTrace, mappedStackTrace } = this.state;
|
||||||
window.location.reload();
|
const textarea = document.createElement('textarea');
|
||||||
}
|
|
||||||
|
let contents = [errorMessage, stackTrace];
|
||||||
|
if (mappedStackTrace) {
|
||||||
|
contents.push(mappedStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.textContent = contents.join('\n\n\n');
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
|
||||||
|
try {
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ copied: true });
|
||||||
|
setTimeout(() => this.setState({ copied: false }), 700);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { hasError, errorMessage, stackTrace, mappedStackTrace, componentStack } = this.state;
|
const { hasError, copied, errorMessage } = this.state;
|
||||||
|
|
||||||
if (!hasError) return this.props.children;
|
if (!hasError) {
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
|
||||||
const likelyBrowserAddonIssue = errorMessage && errorMessage.includes('NotFoundError');
|
const likelyBrowserAddonIssue = errorMessage && errorMessage.includes('NotFoundError');
|
||||||
|
|
||||||
let debugInfo = '';
|
|
||||||
if (stackTrace) {
|
|
||||||
debugInfo += 'Stack trace\n-----------\n\n```\n' + errorMessage + '\n' + stackTrace.toString() + '\n```';
|
|
||||||
}
|
|
||||||
if (mappedStackTrace) {
|
|
||||||
debugInfo += 'Mapped stack trace\n-----------\n\n```\n' + errorMessage + '\n' + mappedStackTrace.toString() + '\n```';
|
|
||||||
}
|
|
||||||
if (componentStack) {
|
|
||||||
if (debugInfo) {
|
|
||||||
debugInfo += '\n\n\n';
|
|
||||||
}
|
|
||||||
debugInfo += 'React component stack\n---------------------\n\n```\n' + componentStack.toString() + '\n```';
|
|
||||||
}
|
|
||||||
|
|
||||||
let issueTracker = source_url;
|
|
||||||
if (source_url.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/?$/)) {
|
|
||||||
issueTracker = source_url + '/issues';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div tabIndex={-1}>
|
<div className='error-boundary'>
|
||||||
<div className='error-boundary'>
|
<div>
|
||||||
<h1><FormattedMessage id='web_app_crash.title' defaultMessage="We're sorry, but something went wrong with the Mastodon app." /></h1>
|
<p className='error-boundary__error'>
|
||||||
<p>
|
{ likelyBrowserAddonIssue ? (
|
||||||
<FormattedMessage id='web_app_crash.content' defaultMessage='You could try any of the following:' />
|
<FormattedMessage id='error.unexpected_crash.explanation_addons' defaultMessage='This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.' />
|
||||||
</p>
|
) : (
|
||||||
<ul>
|
<FormattedMessage id='error.unexpected_crash.explanation' defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' />
|
||||||
{ likelyBrowserAddonIssue && (
|
|
||||||
<li>
|
|
||||||
<FormattedMessage
|
|
||||||
id='web_app_crash.disable_addons'
|
|
||||||
defaultMessage='Disable browser add-ons or built-in translation tools'
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
) }
|
|
||||||
<li>
|
|
||||||
<FormattedMessage
|
|
||||||
id='web_app_crash.report_issue'
|
|
||||||
defaultMessage='Report a bug in the {issuetracker}'
|
|
||||||
values={{ issuetracker: <a href={issueTracker} rel='noopener noreferrer' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }}
|
|
||||||
/>
|
|
||||||
{ debugInfo !== '' && (
|
|
||||||
<details>
|
|
||||||
<summary><FormattedMessage id='web_app_crash.debug_info' defaultMessage='Debug information' /></summary>
|
|
||||||
<textarea
|
|
||||||
className='web_app_crash-stacktrace'
|
|
||||||
value={debugInfo}
|
|
||||||
rows='10'
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</details>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<FormattedMessage
|
|
||||||
id='web_app_crash.reload_page'
|
|
||||||
defaultMessage='{reload} the current page'
|
|
||||||
values={{ reload: <a href='#' onClick={this.handleReload}><FormattedMessage id='web_app_crash.reload' defaultMessage='Reload' /></a> }}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
{ preferencesLink !== undefined && (
|
|
||||||
<li>
|
|
||||||
<FormattedMessage
|
|
||||||
id='web_app_crash.change_your_settings'
|
|
||||||
defaultMessage='Change your {settings}'
|
|
||||||
values={{ settings: <a href={preferencesLink}><FormattedMessage id='web_app_crash.settings' defaultMessage='settings' /></a> }}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)}
|
)}
|
||||||
</ul>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{ likelyBrowserAddonIssue ? (
|
||||||
|
<FormattedMessage id='error.unexpected_crash.next_steps_addons' defaultMessage='Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' />
|
||||||
|
) : (
|
||||||
|
<FormattedMessage id='error.unexpected_crash.next_steps' defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' />
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? 'copied' : ''}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Helmet>
|
<Helmet>
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const GIFV: React.FC<Props> = ({
|
||||||
onClick();
|
onClick();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onClick]
|
[onClick],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -10,7 +10,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||||
|
|
||||||
import ShortNumber from 'flavours/glitch/components/short_number';
|
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||||
|
|
||||||
import Permalink from './permalink';
|
import Permalink from './permalink';
|
||||||
|
|
|
@ -354,7 +354,10 @@ class MediaGallery extends PureComponent {
|
||||||
if (uncached) {
|
if (uncached) {
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<button type='button' disabled className='spoiler-button__overlay'>
|
<button type='button' disabled className='spoiler-button__overlay'>
|
||||||
<span className='spoiler-button__overlay__label'><FormattedMessage id='status.uncached_media_warning' defaultMessage='Not available' /></span>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
<FormattedMessage id='status.uncached_media_warning' defaultMessage='Preview not available' />
|
||||||
|
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.open' defaultMessage='Click to open' /></span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
} else if (visible) {
|
} else if (visible) {
|
||||||
|
@ -362,7 +365,10 @@ class MediaGallery extends PureComponent {
|
||||||
} else {
|
} else {
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||||
<span className='spoiler-button__overlay__label'>{sensitive ? <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /> : <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />}</span>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
{sensitive ? <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /> : <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />}
|
||||||
|
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ export const timeAgoString = (
|
||||||
now: number,
|
now: number,
|
||||||
year: number,
|
year: number,
|
||||||
timeGiven: boolean,
|
timeGiven: boolean,
|
||||||
short?: boolean
|
short?: boolean,
|
||||||
) => {
|
) => {
|
||||||
const delta = now - date.getTime();
|
const delta = now - date.getTime();
|
||||||
|
|
||||||
|
@ -118,28 +118,28 @@ export const timeAgoString = (
|
||||||
relativeTime = intl.formatMessage(messages.today);
|
relativeTime = intl.formatMessage(messages.today);
|
||||||
} else if (delta < 10 * SECOND) {
|
} else if (delta < 10 * SECOND) {
|
||||||
relativeTime = intl.formatMessage(
|
relativeTime = intl.formatMessage(
|
||||||
short ? messages.just_now : messages.just_now_full
|
short ? messages.just_now : messages.just_now_full,
|
||||||
);
|
);
|
||||||
} else if (delta < 7 * DAY) {
|
} else if (delta < 7 * DAY) {
|
||||||
if (delta < MINUTE) {
|
if (delta < MINUTE) {
|
||||||
relativeTime = intl.formatMessage(
|
relativeTime = intl.formatMessage(
|
||||||
short ? messages.seconds : messages.seconds_full,
|
short ? messages.seconds : messages.seconds_full,
|
||||||
{ number: Math.floor(delta / SECOND) }
|
{ number: Math.floor(delta / SECOND) },
|
||||||
);
|
);
|
||||||
} else if (delta < HOUR) {
|
} else if (delta < HOUR) {
|
||||||
relativeTime = intl.formatMessage(
|
relativeTime = intl.formatMessage(
|
||||||
short ? messages.minutes : messages.minutes_full,
|
short ? messages.minutes : messages.minutes_full,
|
||||||
{ number: Math.floor(delta / MINUTE) }
|
{ number: Math.floor(delta / MINUTE) },
|
||||||
);
|
);
|
||||||
} else if (delta < DAY) {
|
} else if (delta < DAY) {
|
||||||
relativeTime = intl.formatMessage(
|
relativeTime = intl.formatMessage(
|
||||||
short ? messages.hours : messages.hours_full,
|
short ? messages.hours : messages.hours_full,
|
||||||
{ number: Math.floor(delta / HOUR) }
|
{ number: Math.floor(delta / HOUR) },
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
relativeTime = intl.formatMessage(
|
relativeTime = intl.formatMessage(
|
||||||
short ? messages.days : messages.days_full,
|
short ? messages.days : messages.days_full,
|
||||||
{ number: Math.floor(delta / DAY) }
|
{ number: Math.floor(delta / DAY) },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (date.getFullYear() === year) {
|
} else if (date.getFullYear() === year) {
|
||||||
|
@ -158,7 +158,7 @@ const timeRemainingString = (
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
date: Date,
|
date: Date,
|
||||||
now: number,
|
now: number,
|
||||||
timeGiven = true
|
timeGiven = true,
|
||||||
) => {
|
) => {
|
||||||
const delta = date.getTime() - now;
|
const delta = date.getTime() - now;
|
||||||
|
|
||||||
|
|
46
app/javascript/flavours/glitch/components/router.tsx
Normal file
46
app/javascript/flavours/glitch/components/router.tsx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { createBrowserHistory } from 'history';
|
||||||
|
import { Router as OriginalRouter } from 'react-router';
|
||||||
|
|
||||||
|
import { layoutFromWindow } from 'flavours/glitch/is_mobile';
|
||||||
|
|
||||||
|
interface MastodonLocationState {
|
||||||
|
fromMastodon?: boolean;
|
||||||
|
mastodonModalKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const browserHistory = createBrowserHistory<
|
||||||
|
MastodonLocationState | undefined
|
||||||
|
>();
|
||||||
|
const originalPush = browserHistory.push.bind(browserHistory);
|
||||||
|
const originalReplace = browserHistory.replace.bind(browserHistory);
|
||||||
|
|
||||||
|
browserHistory.push = (path: string, state?: MastodonLocationState) => {
|
||||||
|
state = state ?? {};
|
||||||
|
state.fromMastodon = true;
|
||||||
|
|
||||||
|
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
|
||||||
|
originalPush(`/deck${path}`, state);
|
||||||
|
} else {
|
||||||
|
originalPush(path, state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
browserHistory.replace = (path: string, state?: MastodonLocationState) => {
|
||||||
|
if (browserHistory.location.state?.fromMastodon) {
|
||||||
|
state = state ?? {};
|
||||||
|
state.fromMastodon = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
|
||||||
|
originalReplace(`/deck${path}`, state);
|
||||||
|
} else {
|
||||||
|
originalReplace(path, state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Router: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
|
return <OriginalRouter history={browserHistory}>{children}</OriginalRouter>;
|
||||||
|
};
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchServer } from 'flavours/glitch/actions/server';
|
import { fetchServer } from 'flavours/glitch/actions/server';
|
||||||
import { ServerHeroImage } from 'flavours/glitch/components/server_hero_image';
|
import { ServerHeroImage } from 'flavours/glitch/components/server_hero_image';
|
||||||
import ShortNumber from 'flavours/glitch/components/short_number';
|
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||||
import Account from 'flavours/glitch/containers/account_container';
|
import Account from 'flavours/glitch/containers/account_container';
|
||||||
import { domain } from 'flavours/glitch/initial_state';
|
import { domain } from 'flavours/glitch/initial_state';
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
|
||||||
|
|
||||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback ShortNumberRenderer
|
|
||||||
* @param {JSX.Element} displayNumber Number to display
|
|
||||||
* @param {number} pluralReady Number used for pluralization
|
|
||||||
* @returns {JSX.Element} Final render of number
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} ShortNumberProps
|
|
||||||
* @property {number} value Number to display in short variant
|
|
||||||
* @property {ShortNumberRenderer} [renderer]
|
|
||||||
* Custom renderer for numbers, provided as a prop. If another renderer
|
|
||||||
* passed as a child of this component, this prop won't be used.
|
|
||||||
* @property {ShortNumberRenderer} [children]
|
|
||||||
* Custom renderer for numbers, provided as a child. If another renderer
|
|
||||||
* passed as a prop of this component, this one will be used instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders short big number to a shorter version
|
|
||||||
* @param {ShortNumberProps} param0 Props for the component
|
|
||||||
* @returns {JSX.Element} Rendered number
|
|
||||||
*/
|
|
||||||
function ShortNumber({ value, renderer, children }) {
|
|
||||||
const shortNumber = toShortNumber(value);
|
|
||||||
const [, division] = shortNumber;
|
|
||||||
|
|
||||||
if (children != null && renderer != null) {
|
|
||||||
console.warn('Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const customRenderer = children != null ? children : renderer;
|
|
||||||
|
|
||||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
|
||||||
|
|
||||||
return customRenderer != null
|
|
||||||
? customRenderer(displayNumber, pluralReady(value, division))
|
|
||||||
: displayNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortNumber.propTypes = {
|
|
||||||
value: PropTypes.number.isRequired,
|
|
||||||
renderer: PropTypes.func,
|
|
||||||
children: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} ShortNumberCounterProps
|
|
||||||
* @property {import('../utils/number').ShortNumber} value Short number
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders short number into corresponding localizable react fragment
|
|
||||||
* @param {ShortNumberCounterProps} param0 Props for the component
|
|
||||||
* @returns {JSX.Element} FormattedMessage ready to be embedded in code
|
|
||||||
*/
|
|
||||||
function ShortNumberCounter({ value }) {
|
|
||||||
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
|
||||||
|
|
||||||
const count = (
|
|
||||||
<FormattedNumber
|
|
||||||
value={rawNumber}
|
|
||||||
maximumFractionDigits={maxFractionDigits}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
let values = { count, rawNumber };
|
|
||||||
|
|
||||||
switch (unit) {
|
|
||||||
case DECIMAL_UNITS.THOUSAND: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.thousand'
|
|
||||||
defaultMessage='{count}K'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DECIMAL_UNITS.MILLION: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.million'
|
|
||||||
defaultMessage='{count}M'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DECIMAL_UNITS.BILLION: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.billion'
|
|
||||||
defaultMessage='{count}B'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Not sure if we should go farther - @Sasha-Sorokin
|
|
||||||
default: return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortNumberCounter.propTypes = {
|
|
||||||
value: PropTypes.arrayOf(PropTypes.number),
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ShortNumber);
|
|
90
app/javascript/flavours/glitch/components/short_number.tsx
Normal file
90
app/javascript/flavours/glitch/components/short_number.tsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||||
|
|
||||||
|
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
||||||
|
|
||||||
|
type ShortNumberRenderer = (
|
||||||
|
displayNumber: JSX.Element,
|
||||||
|
pluralReady: number,
|
||||||
|
) => JSX.Element;
|
||||||
|
|
||||||
|
interface ShortNumberProps {
|
||||||
|
value: number;
|
||||||
|
renderer?: ShortNumberRenderer;
|
||||||
|
children?: ShortNumberRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({
|
||||||
|
value,
|
||||||
|
renderer,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const shortNumber = toShortNumber(value);
|
||||||
|
const [, division] = shortNumber;
|
||||||
|
|
||||||
|
if (children && renderer) {
|
||||||
|
console.warn(
|
||||||
|
'Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const customRenderer = children ?? renderer ?? null;
|
||||||
|
|
||||||
|
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
customRenderer?.(displayNumber, pluralReady(value, division)) ??
|
||||||
|
displayNumber
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const ShortNumber = memo(ShortNumberRenderer);
|
||||||
|
|
||||||
|
interface ShortNumberCounterProps {
|
||||||
|
value: number[];
|
||||||
|
}
|
||||||
|
const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
|
||||||
|
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
||||||
|
|
||||||
|
const count = (
|
||||||
|
<FormattedNumber
|
||||||
|
value={rawNumber}
|
||||||
|
maximumFractionDigits={maxFractionDigits}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const values = { count, rawNumber };
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case DECIMAL_UNITS.THOUSAND: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.thousand'
|
||||||
|
defaultMessage='{count}K'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case DECIMAL_UNITS.MILLION: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.million'
|
||||||
|
defaultMessage='{count}M'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case DECIMAL_UNITS.BILLION: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.billion'
|
||||||
|
defaultMessage='{count}B'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Not sure if we should go farther - @Sasha-Sorokin
|
||||||
|
default:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
|
@ -32,7 +32,7 @@ const messages = defineMessages({
|
||||||
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
||||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||||
|
@ -212,9 +212,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
||||||
const { permissions } = this.context.identity;
|
const { permissions, signedIn } = this.context.identity;
|
||||||
|
|
||||||
const anonymousAccess = !me;
|
|
||||||
const mutingConversation = status.get('muted');
|
const mutingConversation = status.get('muted');
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||||
|
@ -238,53 +237,55 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publicStatus) {
|
if (publicStatus && (signedIn || !isRemote)) {
|
||||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.push(null);
|
if (signedIn) {
|
||||||
|
|
||||||
if (writtenByMe && pinnableStatus) {
|
|
||||||
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
|
||||||
menu.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writtenByMe || withDismiss) {
|
|
||||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
|
||||||
menu.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writtenByMe) {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true });
|
|
||||||
} else {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
|
|
||||||
if (!this.props.onFilter) {
|
if (writtenByMe && pinnableStatus) {
|
||||||
menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick, dangerous: true });
|
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick, dangerous: true });
|
if (writtenByMe || withDismiss) {
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick, dangerous: true });
|
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport, dangerous: true });
|
|
||||||
|
|
||||||
if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
}
|
||||||
if (accountAdminLink !== undefined) {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
|
if (writtenByMe) {
|
||||||
}
|
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
||||||
if (statusAdminLink !== undefined) {
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
||||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
|
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true });
|
||||||
}
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
||||||
|
menu.push(null);
|
||||||
|
|
||||||
|
if (!this.props.onFilter) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick, dangerous: true });
|
||||||
|
menu.push(null);
|
||||||
}
|
}
|
||||||
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
|
||||||
const domain = status.getIn(['account', 'acct']).split('@')[1];
|
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick, dangerous: true });
|
||||||
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick, dangerous: true });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport, dangerous: true });
|
||||||
|
|
||||||
|
if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||||
|
menu.push(null);
|
||||||
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||||
|
if (accountAdminLink !== undefined) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
|
||||||
|
}
|
||||||
|
if (statusAdminLink !== undefined) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||||
|
const domain = status.getIn(['account', 'acct']).split('@')[1];
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,14 +329,13 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
<IconButton className='status__action-bar-button' disabled={!publicStatus} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.quote)} icon='quote-right' onClick={this.handleQuoteClick} />
|
<IconButton className='status__action-bar-button' disabled={!publicStatus} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.quote)} icon='quote-right' onClick={this.handleQuoteClick} />
|
||||||
|
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||||
<IconButton className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
|
<IconButton className='status__action-bar-button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
|
||||||
|
|
||||||
{filterButton}
|
{filterButton}
|
||||||
|
|
||||||
<div className='status__action-bar-dropdown'>
|
<div className='status__action-bar-dropdown'>
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
scrollKey={scrollKey}
|
scrollKey={scrollKey}
|
||||||
disabled={anonymousAccess}
|
|
||||||
status={status}
|
status={status}
|
||||||
items={menu}
|
items={menu}
|
||||||
icon='ellipsis-h'
|
icon='ellipsis-h'
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue